00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include <stdlib.h>
00034 #include <errno.h>
00035 #include <vlc/vlc.h>
00036
00037 #include <sys/types.h>
00038 #include <errno.h>
00039 #ifdef HAVE_DIRENT_H
00040 # include <dirent.h>
00041 #endif
00042 #ifdef HAVE_SYS_STAT_H
00043 # include <sys/stat.h>
00044 # ifdef HAVE_UNISTD_H
00045 # include <unistd.h>
00046 # endif
00047 #endif
00048
00049
00050 #include "vlc_tls.h"
00051
00052 #include <gcrypt.h>
00053 #include <gnutls/gnutls.h>
00054 #include <gnutls/x509.h>
00055
00056 #define DH_BITS 1024
00057 #define CACHE_EXPIRATION 3600
00058 #define CACHE_SIZE 64
00059
00060
00061
00062
00063 static int Open ( vlc_object_t * );
00064 static void Close( vlc_object_t * );
00065
00066 #define DH_BITS_TEXT N_("Diffie-Hellman prime bits")
00067 #define DH_BITS_LONGTEXT N_( \
00068 "Allows you to modify the Diffie-Hellman prime's number of bits " \
00069 "(used for TLS or SSL-based server-side encryption)." )
00070
00071 #define CACHE_EXPIRATION_TEXT N_("Expiration time for resumed TLS sessions")
00072 #define CACHE_EXPIRATION_LONGTEXT N_( \
00073 "Defines the delay before resumed TLS sessions will be expired " \
00074 "(in seconds)." )
00075
00076 #define CACHE_SIZE_TEXT N_("Number of resumed TLS sessions")
00077 #define CACHE_SIZE_LONGTEXT N_( \
00078 "Allows you to modify the maximum number of resumed TLS sessions that " \
00079 "the cache will hold." )
00080
00081 #define CHECK_CERT_TEXT N_("Check TLS/SSL server certificate validity")
00082 #define CHECK_CERT_LONGTEXT N_( \
00083 "Ensures that server certificate is valid " \
00084 "(i.e. signed by an approved Certificate Authority)." )
00085
00086 #define CHECK_HOSTNAME_TEXT N_("Check TLS/SSL server hostname in certificate")
00087 #define CHECK_HOSTNAME_LONGTEXT N_( \
00088 "Ensures that server hostname in certificate match requested host name." )
00089
00090 vlc_module_begin();
00091 set_shortname( "GnuTLS" );
00092 set_description( _("GnuTLS TLS encryption layer") );
00093 set_capability( "tls", 1 );
00094 set_callbacks( Open, Close );
00095 set_category( CAT_ADVANCED );
00096 set_subcategory( SUBCAT_ADVANCED_MISC );
00097
00098 add_bool( "tls-check-cert", VLC_FALSE, NULL, CHECK_CERT_TEXT,
00099 CHECK_CERT_LONGTEXT, VLC_FALSE );
00100 add_bool( "tls-check-hostname", VLC_FALSE, NULL, CHECK_HOSTNAME_TEXT,
00101 CHECK_HOSTNAME_LONGTEXT, VLC_FALSE );
00102
00103 add_integer( "dh-bits", DH_BITS, NULL, DH_BITS_TEXT,
00104 DH_BITS_LONGTEXT, VLC_TRUE );
00105 add_integer( "tls-cache-expiration", CACHE_EXPIRATION, NULL,
00106 CACHE_EXPIRATION_TEXT, CACHE_EXPIRATION_LONGTEXT, VLC_TRUE );
00107 add_integer( "tls-cache-size", CACHE_SIZE, NULL, CACHE_SIZE_TEXT,
00108 CACHE_SIZE_LONGTEXT, VLC_TRUE );
00109 vlc_module_end();
00110
00111
00112 #define MAX_SESSION_ID 32
00113 #define MAX_SESSION_DATA 1024
00114
00115 typedef struct saved_session_t
00116 {
00117 char id[MAX_SESSION_ID];
00118 char data[MAX_SESSION_DATA];
00119
00120 unsigned i_idlen;
00121 unsigned i_datalen;
00122 } saved_session_t;
00123
00124
00125 typedef struct tls_server_sys_t
00126 {
00127 gnutls_certificate_credentials x509_cred;
00128 gnutls_dh_params dh_params;
00129
00130 struct saved_session_t *p_cache;
00131 struct saved_session_t *p_store;
00132 int i_cache_size;
00133 vlc_mutex_t cache_lock;
00134
00135 int (*pf_handshake2)( tls_session_t * );
00136 } tls_server_sys_t;
00137
00138
00139 typedef struct tls_session_sys_t
00140 {
00141 gnutls_session session;
00142 char *psz_hostname;
00143 vlc_bool_t b_handshaked;
00144 } tls_session_sys_t;
00145
00146
00147 typedef struct tls_client_sys_t
00148 {
00149 struct tls_session_sys_t session;
00150 gnutls_certificate_credentials x509_cred;
00151 } tls_client_sys_t;
00152
00153
00154 static int
00155 _get_Int( vlc_object_t *p_this, const char *var )
00156 {
00157 vlc_value_t value;
00158
00159 if( var_Get( p_this, var, &value ) != VLC_SUCCESS )
00160 {
00161 var_Create( p_this, var, VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
00162 var_Get( p_this, var, &value );
00163 }
00164
00165 return value.i_int;
00166 }
00167
00168 static int
00169 _get_Bool( vlc_object_t *p_this, const char *var )
00170 {
00171 vlc_value_t value;
00172
00173 if( var_Get( p_this, var, &value ) != VLC_SUCCESS )
00174 {
00175 var_Create( p_this, var, VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
00176 var_Get( p_this, var, &value );
00177 }
00178
00179 return value.b_bool;
00180 }
00181
00182 #define get_Int( a, b ) _get_Int( (vlc_object_t *)(a), (b) )
00183 #define get_Bool( a, b ) _get_Bool( (vlc_object_t *)(a), (b) )
00184
00185
00186
00187
00188
00189
00190
00191 static int
00192 gnutls_Send( void *p_session, const void *buf, int i_length )
00193 {
00194 int val;
00195 tls_session_sys_t *p_sys;
00196
00197 p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
00198
00199 val = gnutls_record_send( p_sys->session, buf, i_length );
00200
00201 return val < 0 ? -1 : val;
00202 }
00203
00204
00205
00206
00207
00208
00209
00210 static int
00211 gnutls_Recv( void *p_session, void *buf, int i_length )
00212 {
00213 int val;
00214 tls_session_sys_t *p_sys;
00215
00216 p_sys = (tls_session_sys_t *)(((tls_session_t *)p_session)->p_sys);
00217
00218 val = gnutls_record_recv( p_sys->session, buf, i_length );
00219
00220 return val < 0 ? -1 : val;
00221 }
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232 static int
00233 gnutls_ContinueHandshake( tls_session_t *p_session)
00234 {
00235 tls_session_sys_t *p_sys;
00236 int val;
00237
00238 p_sys = (tls_session_sys_t *)(p_session->p_sys);
00239
00240
00241 val = gnutls_handshake( p_sys->session );
00242 if( ( val == GNUTLS_E_AGAIN ) || ( val == GNUTLS_E_INTERRUPTED ) )
00243 return 1 + gnutls_record_get_direction( p_sys->session );
00244
00245 if( val < 0 )
00246 {
00247 msg_Err( p_session, "TLS handshake failed : %s",
00248 gnutls_strerror( val ) );
00249 p_session->pf_close( p_session );
00250 return -1;
00251 }
00252
00253 p_sys->b_handshaked = VLC_TRUE;
00254 return 0;
00255 }
00256
00257 static int
00258 gnutls_HandshakeAndValidate( tls_session_t *p_session )
00259 {
00260 int val;
00261
00262 val = gnutls_ContinueHandshake( p_session );
00263 if( val == 0 )
00264 {
00265 unsigned status;
00266 gnutls_x509_crt cert;
00267 const gnutls_datum *p_data;
00268 tls_session_sys_t *p_sys;
00269
00270 p_sys = (tls_session_sys_t *)(p_session->p_sys);
00271
00272 val = gnutls_certificate_verify_peers2( p_sys->session, &status );
00273
00274 if( val )
00275 {
00276 msg_Err( p_session, "TLS certificate verification failed : %s",
00277 gnutls_strerror( val ) );
00278 p_session->pf_close( p_session );
00279 return -1;
00280 }
00281
00282 if( status )
00283 {
00284 msg_Warn( p_session, "TLS session : access denied" );
00285 if( status & GNUTLS_CERT_INVALID )
00286 msg_Dbg( p_session, "certificate could not be verified" );
00287 if( status & GNUTLS_CERT_REVOKED )
00288 msg_Dbg( p_session, "certificate was revoked" );
00289 if( status & GNUTLS_CERT_SIGNER_NOT_FOUND )
00290 msg_Dbg( p_session, "certificate's signer was not found" );
00291 if( status & GNUTLS_CERT_SIGNER_NOT_CA )
00292 msg_Dbg( p_session, "certificate's signer is not a CA" );
00293 p_session->pf_close( p_session );
00294 return -1;
00295 }
00296
00297 msg_Dbg( p_session, "TLS certificate verified" );
00298 if( p_sys->psz_hostname == NULL )
00299 return 0;
00300
00301
00302 p_data = gnutls_certificate_get_peers( p_sys->session, &status );
00303 if( p_data == NULL )
00304 {
00305 msg_Err( p_session, "TLS peer certificate not available" );
00306 p_session->pf_close( p_session );
00307 return -1;
00308 }
00309
00310 val = gnutls_x509_crt_init( &cert );
00311 if( val )
00312 {
00313 msg_Err( p_session, "x509 fatal error : %s",
00314 gnutls_strerror( val ) );
00315 p_session->pf_close( p_session );
00316 return -1;
00317 }
00318
00319 val = gnutls_x509_crt_import( cert, p_data, GNUTLS_X509_FMT_DER );
00320 if( val )
00321 {
00322 msg_Err( p_session, "x509 certificate import error : %s",
00323 gnutls_strerror( val ) );
00324 gnutls_x509_crt_deinit( cert );
00325 p_session->pf_close( p_session );
00326 return -1;
00327 }
00328
00329 if( gnutls_x509_crt_check_hostname( cert, p_sys->psz_hostname ) == 0 )
00330 {
00331 msg_Err( p_session, "x509 certificate does not match \"%s\"",
00332 p_sys->psz_hostname );
00333 gnutls_x509_crt_deinit( cert );
00334 p_session->pf_close( p_session );
00335 return -1;
00336 }
00337
00338 gnutls_x509_crt_deinit( cert );
00339
00340 msg_Dbg( p_session, "x509 hostname verified" );
00341 return 0;
00342 }
00343
00344 return val;
00345 }
00346
00347 static int
00348 gnutls_BeginHandshake( tls_session_t *p_session, int fd,
00349 const char *psz_hostname )
00350 {
00351 tls_session_sys_t *p_sys;
00352
00353 p_sys = (tls_session_sys_t *)(p_session->p_sys);
00354
00355 gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)fd);
00356
00357 if( psz_hostname != NULL )
00358 {
00359 gnutls_server_name_set( p_sys->session, GNUTLS_NAME_DNS, psz_hostname,
00360 strlen( psz_hostname ) );
00361 if( get_Bool( p_session, "tls-check-hostname" ) )
00362 {
00363 p_sys->psz_hostname = strdup( psz_hostname );
00364 if( p_sys->psz_hostname == NULL )
00365 {
00366 p_session->pf_close( p_session );
00367 return -1;
00368 }
00369 }
00370 }
00371
00372 return p_session->pf_handshake2( p_session );
00373 }
00374
00375
00376
00377
00378
00379
00380 static void
00381 gnutls_SessionClose( tls_session_t *p_session )
00382 {
00383 tls_session_sys_t *p_sys;
00384
00385 p_sys = (tls_session_sys_t *)(p_session->p_sys);
00386
00387 if( p_sys->b_handshaked == VLC_TRUE )
00388 gnutls_bye( p_sys->session, GNUTLS_SHUT_WR );
00389 gnutls_deinit( p_sys->session );
00390
00391 if( p_sys->psz_hostname != NULL )
00392 free( p_sys->psz_hostname );
00393
00394 vlc_object_detach( p_session );
00395 vlc_object_destroy( p_session );
00396
00397 free( p_sys );
00398 }
00399
00400 static void
00401 gnutls_ClientDelete( tls_session_t *p_session )
00402 {
00403
00404 gnutls_certificate_credentials x509_cred =
00405 ((tls_client_sys_t *)(p_session->p_sys))->x509_cred;
00406
00407 gnutls_SessionClose( p_session );
00408
00409
00410 gnutls_certificate_free_credentials( x509_cred );
00411 }
00412
00413 inline int
00414 is_regular( const char *psz_filename )
00415 {
00416 #ifdef HAVE_SYS_STAT_H
00417 struct stat st;
00418
00419 return ( stat( psz_filename, &st ) == 0 )
00420 && S_ISREG( st.st_mode );
00421 #else
00422 return 1;
00423 #endif
00424 }
00425
00426 static int
00427 gnutls_Addx509Directory( vlc_object_t *p_this,
00428 gnutls_certificate_credentials cred,
00429 const char *psz_dirname,
00430 vlc_bool_t private )
00431 {
00432 DIR* dir;
00433 struct dirent *p_ent;
00434 int i_len;
00435
00436 if( *psz_dirname == '\0' )
00437 psz_dirname = ".";
00438
00439 dir = opendir( psz_dirname );
00440 if( dir == NULL )
00441 {
00442 msg_Warn( p_this, "Cannot open directory (%s) : %s", psz_dirname,
00443 strerror( errno ) );
00444 return VLC_EGENERIC;
00445 }
00446
00447 i_len = strlen( psz_dirname ) + 2;
00448
00449 while( ( p_ent = readdir( dir ) ) != NULL )
00450 {
00451 char *psz_filename;
00452
00453 psz_filename = (char *)malloc( i_len + strlen( p_ent->d_name ) );
00454 if( psz_filename == NULL )
00455 {
00456 closedir( dir );
00457 return VLC_ENOMEM;
00458 }
00459
00460 sprintf( psz_filename, "%s/%s", psz_dirname, p_ent->d_name );
00461
00462 if( is_regular( psz_filename ) )
00463 {
00464 int i;
00465
00466 i = (private)
00467 ? gnutls_certificate_set_x509_key_file( cred, psz_filename,
00468 psz_filename,
00469 GNUTLS_X509_FMT_PEM )
00470 : gnutls_certificate_set_x509_trust_file( cred, psz_filename,
00471 GNUTLS_X509_FMT_PEM
00472 );
00473 if( i < 0 )
00474 {
00475 msg_Warn( p_this, "Cannot add x509 certificate (%s) : %s",
00476 psz_filename, gnutls_strerror( i ) );
00477 }
00478 }
00479 free( psz_filename );
00480 }
00481
00482 closedir( dir );
00483 return VLC_SUCCESS;
00484 }
00485
00486
00487
00488
00489
00490
00491 static tls_session_t *
00492 gnutls_ClientCreate( tls_t *p_tls )
00493 {
00494 tls_session_t *p_session = NULL;
00495 tls_client_sys_t *p_sys = NULL;
00496 int i_val;
00497 const int cert_type_priority[3] =
00498 {
00499 GNUTLS_CRT_X509,
00500 0
00501 };
00502
00503 p_sys = (tls_client_sys_t *)malloc( sizeof(struct tls_client_sys_t) );
00504 if( p_sys == NULL )
00505 return NULL;
00506
00507 p_session = (struct tls_session_t *)vlc_object_create ( p_tls, sizeof(struct tls_session_t) );
00508 if( p_session == NULL )
00509 {
00510 free( p_sys );
00511 return NULL;
00512 }
00513
00514 p_session->p_sys = p_sys;
00515 p_session->sock.p_sys = p_session;
00516 p_session->sock.pf_send = gnutls_Send;
00517 p_session->sock.pf_recv = gnutls_Recv;
00518 p_session->pf_handshake = gnutls_BeginHandshake;
00519 p_session->pf_close = gnutls_ClientDelete;
00520
00521 p_sys->session.b_handshaked = VLC_FALSE;
00522 p_sys->session.psz_hostname = NULL;
00523
00524 vlc_object_attach( p_session, p_tls );
00525
00526 i_val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
00527 if( i_val != 0 )
00528 {
00529 msg_Err( p_tls, "Cannot allocate X509 credentials : %s",
00530 gnutls_strerror( i_val ) );
00531 goto error;
00532 }
00533
00534 if( get_Bool( p_tls, "tls-check-cert" ) )
00535 {
00536
00537 char *psz_path;
00538 const char *psz_homedir;
00539
00540 psz_homedir = p_tls->p_vlc->psz_homedir;
00541 psz_path = (char *)malloc( strlen( psz_homedir )
00542 + sizeof( CONFIG_DIR ) + 12 );
00543 if( psz_path == NULL )
00544 {
00545 gnutls_certificate_free_credentials( p_sys->x509_cred );
00546 goto error;
00547 }
00548
00549 sprintf( psz_path, "%s/"CONFIG_DIR"/ssl/certs", psz_homedir );
00550 gnutls_Addx509Directory( (vlc_object_t *)p_session, p_sys->x509_cred,
00551 psz_path, VLC_FALSE );
00552
00553 free( psz_path );
00554 p_session->pf_handshake2 = gnutls_HandshakeAndValidate;
00555 }
00556 else
00557 p_session->pf_handshake2 = gnutls_ContinueHandshake;
00558
00559 {
00560
00561 char *psz_path;
00562 const char *psz_homedir;
00563
00564 psz_homedir = p_tls->p_vlc->psz_homedir;
00565 psz_path = (char *)malloc( strlen( psz_homedir )
00566 + sizeof( CONFIG_DIR ) + 14 );
00567 if( psz_path == NULL )
00568 {
00569 gnutls_certificate_free_credentials( p_sys->x509_cred );
00570 goto error;
00571 }
00572
00573 sprintf( psz_path, "%s/"CONFIG_DIR"/ssl/private", psz_homedir );
00574 gnutls_Addx509Directory( (vlc_object_t *)p_session, p_sys->x509_cred,
00575 psz_path, VLC_TRUE );
00576
00577 free( psz_path );
00578 }
00579
00580 i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT );
00581 if( i_val != 0 )
00582 {
00583 msg_Err( p_tls, "Cannot initialize TLS session : %s",
00584 gnutls_strerror( i_val ) );
00585 gnutls_certificate_free_credentials( p_sys->x509_cred );
00586 goto error;
00587 }
00588
00589 i_val = gnutls_set_default_priority( p_sys->session.session );
00590 if( i_val < 0 )
00591 {
00592 msg_Err( p_tls, "Cannot set ciphers priorities : %s",
00593 gnutls_strerror( i_val ) );
00594 gnutls_deinit( p_sys->session.session );
00595 gnutls_certificate_free_credentials( p_sys->x509_cred );
00596 goto error;
00597 }
00598
00599 i_val = gnutls_certificate_type_set_priority( p_sys->session.session,
00600 cert_type_priority );
00601 if( i_val < 0 )
00602 {
00603 msg_Err( p_tls, "Cannot set certificate type priorities : %s",
00604 gnutls_strerror( i_val ) );
00605 gnutls_deinit( p_sys->session.session );
00606 gnutls_certificate_free_credentials( p_sys->x509_cred );
00607 goto error;
00608 }
00609
00610 i_val = gnutls_credentials_set( p_sys->session.session,
00611 GNUTLS_CRD_CERTIFICATE,
00612 p_sys->x509_cred );
00613 if( i_val < 0 )
00614 {
00615 msg_Err( p_tls, "Cannot set TLS session credentials : %s",
00616 gnutls_strerror( i_val ) );
00617 gnutls_deinit( p_sys->session.session );
00618 gnutls_certificate_free_credentials( p_sys->x509_cred );
00619 goto error;
00620 }
00621
00622 return p_session;
00623
00624 error:
00625 vlc_object_detach( p_session );
00626 vlc_object_destroy( p_session );
00627 free( p_sys );
00628
00629 return NULL;
00630 }
00631
00632
00633
00634
00635
00636 static int cb_store( void *p_server, gnutls_datum key, gnutls_datum data )
00637 {
00638 tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
00639
00640 if( ( p_sys->i_cache_size == 0 )
00641 || ( key.size > MAX_SESSION_ID )
00642 || ( data.size > MAX_SESSION_DATA ) )
00643 return -1;
00644
00645 vlc_mutex_lock( &p_sys->cache_lock );
00646
00647 memcpy( p_sys->p_store->id, key.data, key.size);
00648 memcpy( p_sys->p_store->data, data.data, data.size );
00649 p_sys->p_store->i_idlen = key.size;
00650 p_sys->p_store->i_datalen = data.size;
00651
00652 p_sys->p_store++;
00653 if( ( p_sys->p_store - p_sys->p_cache ) == p_sys->i_cache_size )
00654 p_sys->p_store = p_sys->p_cache;
00655
00656 vlc_mutex_unlock( &p_sys->cache_lock );
00657
00658 return 0;
00659 }
00660
00661
00662 static const gnutls_datum err_datum = { NULL, 0 };
00663
00664 static gnutls_datum cb_fetch( void *p_server, gnutls_datum key )
00665 {
00666 tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
00667 saved_session_t *p_session, *p_end;
00668
00669 p_session = p_sys->p_cache;
00670 p_end = p_session + p_sys->i_cache_size;
00671
00672 vlc_mutex_lock( &p_sys->cache_lock );
00673
00674 while( p_session < p_end )
00675 {
00676 if( ( p_session->i_idlen == key.size )
00677 && !memcmp( p_session->id, key.data, key.size ) )
00678 {
00679 gnutls_datum data;
00680
00681 data.size = p_session->i_datalen;
00682
00683 data.data = gnutls_malloc( data.size );
00684 if( data.data == NULL )
00685 {
00686 vlc_mutex_unlock( &p_sys->cache_lock );
00687 return err_datum;
00688 }
00689
00690 memcpy( data.data, p_session->data, data.size );
00691 vlc_mutex_unlock( &p_sys->cache_lock );
00692 return data;
00693 }
00694 p_session++;
00695 }
00696
00697 vlc_mutex_unlock( &p_sys->cache_lock );
00698
00699 return err_datum;
00700 }
00701
00702
00703 static int cb_delete( void *p_server, gnutls_datum key )
00704 {
00705 tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
00706 saved_session_t *p_session, *p_end;
00707
00708 p_session = p_sys->p_cache;
00709 p_end = p_session + p_sys->i_cache_size;
00710
00711 vlc_mutex_lock( &p_sys->cache_lock );
00712
00713 while( p_session < p_end )
00714 {
00715 if( ( p_session->i_idlen == key.size )
00716 && !memcmp( p_session->id, key.data, key.size ) )
00717 {
00718 p_session->i_datalen = p_session->i_idlen = 0;
00719 vlc_mutex_unlock( &p_sys->cache_lock );
00720 return 0;
00721 }
00722 p_session++;
00723 }
00724
00725 vlc_mutex_unlock( &p_sys->cache_lock );
00726
00727 return -1;
00728 }
00729
00730
00731
00732
00733
00734
00735
00736 static tls_session_t *
00737 gnutls_ServerSessionPrepare( tls_server_t *p_server )
00738 {
00739 tls_session_t *p_session;
00740 tls_server_sys_t *p_server_sys;
00741 gnutls_session session;
00742 int i_val;
00743
00744 p_session = vlc_object_create( p_server, sizeof (struct tls_session_t) );
00745 if( p_session == NULL )
00746 return NULL;
00747
00748 p_session->p_sys = malloc( sizeof(struct tls_session_sys_t) );
00749 if( p_session->p_sys == NULL )
00750 {
00751 vlc_object_destroy( p_session );
00752 return NULL;
00753 }
00754
00755 vlc_object_attach( p_session, p_server );
00756
00757 p_server_sys = (tls_server_sys_t *)p_server->p_sys;
00758 p_session->sock.p_sys = p_session;
00759 p_session->sock.pf_send = gnutls_Send;
00760 p_session->sock.pf_recv = gnutls_Recv;
00761 p_session->pf_handshake = gnutls_BeginHandshake;
00762 p_session->pf_handshake2 = p_server_sys->pf_handshake2;
00763 p_session->pf_close = gnutls_SessionClose;
00764
00765 ((tls_session_sys_t *)p_session->p_sys)->b_handshaked = VLC_FALSE;
00766 ((tls_session_sys_t *)p_session->p_sys)->psz_hostname = NULL;
00767
00768 i_val = gnutls_init( &session, GNUTLS_SERVER );
00769 if( i_val != 0 )
00770 {
00771 msg_Err( p_server, "Cannot initialize TLS session : %s",
00772 gnutls_strerror( i_val ) );
00773 goto error;
00774 }
00775
00776 ((tls_session_sys_t *)p_session->p_sys)->session = session;
00777
00778 i_val = gnutls_set_default_priority( session );
00779 if( i_val < 0 )
00780 {
00781 msg_Err( p_server, "Cannot set ciphers priorities : %s",
00782 gnutls_strerror( i_val ) );
00783 gnutls_deinit( session );
00784 goto error;
00785 }
00786
00787 i_val = gnutls_credentials_set( session, GNUTLS_CRD_CERTIFICATE,
00788 p_server_sys->x509_cred );
00789 if( i_val < 0 )
00790 {
00791 msg_Err( p_server, "Cannot set TLS session credentials : %s",
00792 gnutls_strerror( i_val ) );
00793 gnutls_deinit( session );
00794 goto error;
00795 }
00796
00797 if( p_session->pf_handshake2 == gnutls_HandshakeAndValidate )
00798 gnutls_certificate_server_set_request( session, GNUTLS_CERT_REQUIRE );
00799
00800 gnutls_dh_set_prime_bits( session, get_Int( p_server, "dh-bits" ) );
00801
00802
00803 gnutls_db_set_cache_expiration( session, get_Int( p_server,
00804 "tls-cache-expiration" ) );
00805 gnutls_db_set_retrieve_function( session, cb_fetch );
00806 gnutls_db_set_remove_function( session, cb_delete );
00807 gnutls_db_set_store_function( session, cb_store );
00808 gnutls_db_set_ptr( session, p_server );
00809
00810 return p_session;
00811
00812 error:
00813 free( p_session->p_sys );
00814 vlc_object_detach( p_session );
00815 vlc_object_destroy( p_session );
00816 return NULL;
00817 }
00818
00819
00820
00821
00822
00823
00824
00825 static void
00826 gnutls_ServerDelete( tls_server_t *p_server )
00827 {
00828 tls_server_sys_t *p_sys;
00829 p_sys = (tls_server_sys_t *)p_server->p_sys;
00830
00831 vlc_mutex_destroy( &p_sys->cache_lock );
00832 free( p_sys->p_cache );
00833
00834 vlc_object_detach( p_server );
00835 vlc_object_destroy( p_server );
00836
00837
00838 gnutls_certificate_free_credentials( p_sys->x509_cred );
00839 gnutls_dh_params_deinit( p_sys->dh_params );
00840 free( p_sys );
00841 }
00842
00843
00844
00845
00846
00847
00848
00849
00850 static int
00851 gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
00852 {
00853 int val;
00854 tls_server_sys_t *p_sys;
00855
00856 p_sys = (tls_server_sys_t *)(p_server->p_sys);
00857
00858 val = gnutls_certificate_set_x509_trust_file( p_sys->x509_cred,
00859 psz_ca_path,
00860 GNUTLS_X509_FMT_PEM );
00861 if( val < 0 )
00862 {
00863 msg_Err( p_server, "Cannot add trusted CA (%s) : %s", psz_ca_path,
00864 gnutls_strerror( val ) );
00865 return VLC_EGENERIC;
00866 }
00867 msg_Dbg( p_server, " %d trusted CA added (%s)", val, psz_ca_path );
00868
00869
00870 p_sys->pf_handshake2 = gnutls_HandshakeAndValidate;
00871
00872 return VLC_SUCCESS;
00873 }
00874
00875
00876
00877
00878
00879
00880
00881
00882 static int
00883 gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path )
00884 {
00885 int val;
00886
00887 val = gnutls_certificate_set_x509_crl_file( ((tls_server_sys_t *)
00888 (p_server->p_sys))->x509_cred,
00889 psz_crl_path,
00890 GNUTLS_X509_FMT_PEM );
00891 if( val < 0 )
00892 {
00893 msg_Err( p_server, "Cannot add CRL (%s) : %s", psz_crl_path,
00894 gnutls_strerror( val ) );
00895 return VLC_EGENERIC;
00896 }
00897 msg_Dbg( p_server, "%d CRL added (%s)", val, psz_crl_path );
00898 return VLC_SUCCESS;
00899 }
00900
00901
00902
00903
00904
00905
00906
00907
00908 static tls_server_t *
00909 gnutls_ServerCreate( tls_t *p_tls, const char *psz_cert_path,
00910 const char *psz_key_path )
00911 {
00912 tls_server_t *p_server;
00913 tls_server_sys_t *p_sys;
00914 int val;
00915
00916 msg_Dbg( p_tls, "Creating TLS server" );
00917
00918 p_sys = (tls_server_sys_t *)malloc( sizeof(struct tls_server_sys_t) );
00919 if( p_sys == NULL )
00920 return NULL;
00921
00922 p_sys->i_cache_size = get_Int( p_tls, "tls-cache-size" );
00923 p_sys->p_cache = (struct saved_session_t *)calloc( p_sys->i_cache_size,
00924 sizeof( struct saved_session_t ) );
00925 if( p_sys->p_cache == NULL )
00926 {
00927 free( p_sys );
00928 return NULL;
00929 }
00930 p_sys->p_store = p_sys->p_cache;
00931
00932 p_server = vlc_object_create( p_tls, sizeof(struct tls_server_t) );
00933 if( p_server == NULL )
00934 {
00935 free( p_sys->p_cache );
00936 free( p_sys );
00937 return NULL;
00938 }
00939
00940 vlc_object_attach( p_server, p_tls );
00941
00942 p_server->p_sys = p_sys;
00943 p_server->pf_delete = gnutls_ServerDelete;
00944 p_server->pf_add_CA = gnutls_ServerAddCA;
00945 p_server->pf_add_CRL = gnutls_ServerAddCRL;
00946 p_server->pf_session_prepare = gnutls_ServerSessionPrepare;
00947
00948
00949 p_sys->pf_handshake2 = gnutls_ContinueHandshake;
00950
00951
00952 vlc_mutex_init( p_server, &p_sys->cache_lock );
00953
00954
00955 val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred );
00956 if( val != 0 )
00957 {
00958 msg_Err( p_server, "Cannot allocate X509 credentials : %s",
00959 gnutls_strerror( val ) );
00960 goto error;
00961 }
00962
00963 val = gnutls_certificate_set_x509_key_file( p_sys->x509_cred,
00964 psz_cert_path, psz_key_path,
00965 GNUTLS_X509_FMT_PEM );
00966 if( val < 0 )
00967 {
00968 msg_Err( p_server, "Cannot set certificate chain or private key : %s",
00969 gnutls_strerror( val ) );
00970 gnutls_certificate_free_credentials( p_sys->x509_cred );
00971 goto error;
00972 }
00973
00974
00975
00976
00977
00978 val = gnutls_dh_params_init( &p_sys->dh_params );
00979 if( val >= 0 )
00980 {
00981 msg_Dbg( p_server, "Computing Diffie Hellman ciphers parameters" );
00982 val = gnutls_dh_params_generate2( p_sys->dh_params,
00983 get_Int( p_tls, "dh-bits" ) );
00984 }
00985 if( val < 0 )
00986 {
00987 msg_Err( p_server, "Cannot initialize DH cipher suites : %s",
00988 gnutls_strerror( val ) );
00989 gnutls_certificate_free_credentials( p_sys->x509_cred );
00990 goto error;
00991 }
00992 msg_Dbg( p_server, "Ciphers parameters computed" );
00993
00994 gnutls_certificate_set_dh_params( p_sys->x509_cred, p_sys->dh_params);
00995
00996 return p_server;
00997
00998 error:
00999 vlc_mutex_destroy( &p_sys->cache_lock );
01000 vlc_object_detach( p_server );
01001 vlc_object_destroy( p_server );
01002 free( p_sys );
01003 return NULL;
01004 }
01005
01006
01007
01008
01009
01010 vlc_object_t *__p_gcry_data;
01011
01012 static int gcry_vlc_mutex_init( void **p_sys )
01013 {
01014 int i_val;
01015 vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc( sizeof( vlc_mutex_t ) );
01016
01017 if( p_lock == NULL)
01018 return ENOMEM;
01019
01020 i_val = vlc_mutex_init( __p_gcry_data, p_lock );
01021 if( i_val )
01022 free( p_lock );
01023 else
01024 *p_sys = p_lock;
01025 return i_val;
01026 }
01027
01028 static int gcry_vlc_mutex_destroy( void **p_sys )
01029 {
01030 int i_val;
01031 vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys;
01032
01033 i_val = vlc_mutex_destroy( p_lock );
01034 free( p_lock );
01035 return i_val;
01036 }
01037
01038 static int gcry_vlc_mutex_lock( void **p_sys )
01039 {
01040 return vlc_mutex_lock( (vlc_mutex_t *)*p_sys );
01041 }
01042
01043 static int gcry_vlc_mutex_unlock( void **lock )
01044 {
01045 return vlc_mutex_unlock( (vlc_mutex_t *)*lock );
01046 }
01047
01048 static struct gcry_thread_cbs gcry_threads_vlc =
01049 {
01050 GCRY_THREAD_OPTION_USER,
01051 NULL,
01052 gcry_vlc_mutex_init,
01053 gcry_vlc_mutex_destroy,
01054 gcry_vlc_mutex_lock,
01055 gcry_vlc_mutex_unlock
01056 };
01057
01058
01059
01060
01061
01062 static int
01063 Open( vlc_object_t *p_this )
01064 {
01065 tls_t *p_tls = (tls_t *)p_this;
01066
01067 vlc_value_t lock, count;
01068
01069 var_Create( p_this->p_libvlc, "gnutls_mutex", VLC_VAR_MUTEX );
01070 var_Get( p_this->p_libvlc, "gnutls_mutex", &lock );
01071 vlc_mutex_lock( lock.p_address );
01072
01073
01074 var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
01075 var_Get( p_this->p_libvlc, "gnutls_count", &count);
01076
01077 if( count.i_int == 0)
01078 {
01079 const char *psz_version;
01080
01081 __p_gcry_data = VLC_OBJECT( p_this->p_vlc );
01082
01083 gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc);
01084 if( gnutls_global_init( ) )
01085 {
01086 msg_Warn( p_this, "cannot initialize GnuTLS" );
01087 vlc_mutex_unlock( lock.p_address );
01088 return VLC_EGENERIC;
01089 }
01090
01091
01092
01093
01094 psz_version = gnutls_check_version( "1.0.16" );
01095 if( psz_version == NULL )
01096 {
01097 gnutls_global_deinit( );
01098 vlc_mutex_unlock( lock.p_address );
01099 msg_Err( p_this, "unsupported GnuTLS version" );
01100 return VLC_EGENERIC;
01101 }
01102 msg_Dbg( p_this, "GnuTLS v%s initialized", psz_version );
01103 }
01104
01105 count.i_int++;
01106 var_Set( p_this->p_libvlc, "gnutls_count", count);
01107 vlc_mutex_unlock( lock.p_address );
01108
01109 p_tls->pf_server_create = gnutls_ServerCreate;
01110 p_tls->pf_client_create = gnutls_ClientCreate;
01111 return VLC_SUCCESS;
01112 }
01113
01114
01115
01116
01117
01118 static void
01119 Close( vlc_object_t *p_this )
01120 {
01121
01122
01123
01124 vlc_value_t lock, count;
01125
01126 var_Create( p_this->p_libvlc, "gnutls_mutex", VLC_VAR_MUTEX );
01127 var_Get( p_this->p_libvlc, "gnutls_mutex", &lock );
01128 vlc_mutex_lock( lock.p_address );
01129
01130 var_Create( p_this->p_libvlc, "gnutls_count", VLC_VAR_INTEGER );
01131 var_Get( p_this->p_libvlc, "gnutls_count", &count);
01132 count.i_int--;
01133 var_Set( p_this->p_libvlc, "gnutls_count", count);
01134
01135 if( count.i_int == 0 )
01136 {
01137 gnutls_global_deinit( );
01138 msg_Dbg( p_this, "GnuTLS deinitialized" );
01139 }
01140
01141 vlc_mutex_unlock( lock.p_address );
01142 }