Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

gnutls.c

00001 /*****************************************************************************
00002  * tls.c
00003  *****************************************************************************
00004  * Copyright (C) 2004-2005 Rémi Denis-Courmont
00005  * $Id: gnutls.c 11723 2005-07-13 17:16:09Z courmisch $
00006  *
00007  * Authors: Rémi Denis-Courmont <rem # videolan.org>
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00022  *****************************************************************************/
00023 
00024 /*
00025  * TODO:
00026  * - fix FIXMEs
00027  */
00028 
00029 
00030 /*****************************************************************************
00031  * Preamble
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  * Module descriptor
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  * tls_Send:
00188  *****************************************************************************
00189  * Sends data through a TLS session.
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     /* TODO: handle fatal error */
00201     return val < 0 ? -1 : val;
00202 }
00203 
00204 
00205 /*****************************************************************************
00206  * tls_Recv:
00207  *****************************************************************************
00208  * Receives data through a TLS session.
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     /* TODO: handle fatal error */
00220     return val < 0 ? -1 : val;
00221 }
00222 
00223 
00224 /*****************************************************************************
00225  * tls_Session(Continue)?Handshake:
00226  *****************************************************************************
00227  * Establishes TLS session with a peer through socket <fd>.
00228  * Returns -1 on error (you need not and must not call tls_SessionClose)
00229  * 0 on succesful handshake completion, 1 if more would-be blocking recv is
00230  * needed, 2 if more would-be blocking send is required.
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      /* TODO: handle fatal error */
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         /* certificates chain verification */
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         /* certificate (host)name verification */
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  * tls_SessionClose:
00377  *****************************************************************************
00378  * Terminates TLS session and releases session data.
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     /* On the client-side, credentials are re-allocated per session */
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     /* credentials must be free'd *after* gnutls_deinit() */
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         /* we neglect the race condition here - not security sensitive */
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  * tls_ClientCreate:
00488  *****************************************************************************
00489  * Initializes client-side TLS session data.
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         /* FIXME: support for changing path/using multiple paths */
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         /* FIXME: support for changing path/using multiple paths */
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  * TLS session resumption callbacks
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  * tls_ServerSessionPrepare:
00733  *****************************************************************************
00734  * Initializes server-side TLS session data.
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     /* Session resumption support */
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  * tls_ServerDelete:
00822  *****************************************************************************
00823  * Releases data allocated with tls_ServerCreate.
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     /* all sessions depending on the server are now deinitialized */
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  * tls_ServerAddCA:
00846  *****************************************************************************
00847  * Adds one or more certificate authorities.
00848  * Returns -1 on error, 0 on success.
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     /* enables peer's certificate verification */
00870     p_sys->pf_handshake2 = gnutls_HandshakeAndValidate;
00871 
00872     return VLC_SUCCESS;
00873 }
00874 
00875 
00876 /*****************************************************************************
00877  * tls_ServerAddCRL:
00878  *****************************************************************************
00879  * Adds a certificates revocation list to be sent to TLS clients.
00880  * Returns -1 on error, 0 on success.
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  * tls_ServerCreate:
00904  *****************************************************************************
00905  * Allocates a whole server's TLS credentials.
00906  * Returns NULL on error.
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     /* No certificate validation by default */
00949     p_sys->pf_handshake2 = gnutls_ContinueHandshake;
00950 
00951     /* FIXME: check for errors */
00952     vlc_mutex_init( p_server, &p_sys->cache_lock );
00953 
00954     /* Sets server's credentials */
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     /* FIXME:
00975      * - regenerate these regularly
00976      * - support other ciper suites
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  * gcrypt thread option VLC implementation:
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  * Module initialization
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     /* Initialize GnuTLS only once */
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          * FIXME: in fact, we currently depends on 1.0.17, but it breaks on
01092          * Debian which as a patched 1.0.16 (which we can use).
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  * Module deinitialization
01117  *****************************************************************************/
01118 static void
01119 Close( vlc_object_t *p_this )
01120 {
01121     /*tls_t *p_tls = (tls_t *)p_this;
01122     tls_sys_t *p_sys = (tls_sys_t *)(p_this->p_sys);*/
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 }

Generated on Tue Dec 20 10:14:48 2005 for vlc-0.8.4a by  doxygen 1.4.2