X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;ds=sidebyside;f=modules%2Fmisc%2Fgnutls.c;h=5f6061342010f82cd65a92291d97dade1e4839ce;hb=9389e7ccc5970976e81f069b481cee13f7aacc20;hp=85b5968b3c93c6454ff81c0a0064d8bd75371372;hpb=05719817ee36ceee460e8de783231f0afcda650e;p=vlc diff --git a/modules/misc/gnutls.c b/modules/misc/gnutls.c index 85b5968b3c..5f60613420 100644 --- a/modules/misc/gnutls.c +++ b/modules/misc/gnutls.c @@ -2,9 +2,9 @@ * tls.c ***************************************************************************** * Copyright (C) 2004-2005 VideoLAN - * $Id: httpd.c 8263 2004-07-24 09:06:58Z courmisch $ + * $Id$ * - * Authors: Remi Denis-Courmont + * Authors: Remi Denis-Courmont * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,8 +23,7 @@ /* * TODO: - * - fix FIXMEs, - * - client-side server cert validation (?). + * - fix FIXMEs */ @@ -35,6 +34,19 @@ #include #include +#include +#include +#ifdef HAVE_DIRENT_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +# ifdef HAVE_UNISTD_H +# include +# endif +#endif + + #include "vlc_tls.h" #include @@ -65,14 +77,30 @@ static void Close( vlc_object_t * ); "Allows you to modify the maximum number of resumed TLS sessions that " \ "the cache will hold." ) +#define CHECK_CERT_TEXT N_("Check TLS/SSL server certificate validity") +#define CHECK_CERT_LONGTEXT N_( \ + "Ensures that server certificate is valid " \ + "(ie. signed by an approved Certificate Authority)." ) + +#define CHECK_HOSTNAME_TEXT N_("Check TLS/SSL server hostname in certificate") +#define CHECK_HOSTNAME_LONGTEXT N_( \ + "Ensures that server hostname in certificate match requested host name." ) vlc_module_begin(); + set_shortname( "GnuTLS" ); set_description( _("GnuTLS TLS encryption layer") ); set_capability( "tls", 1 ); set_callbacks( Open, Close ); set_category( CAT_ADVANCED ); set_subcategory( SUBCAT_ADVANCED_MISC ); + add_bool( "tls-check-cert", VLC_FALSE, NULL, CHECK_CERT_TEXT, + CHECK_CERT_LONGTEXT, VLC_FALSE ); +#if 0 + add_bool( "tls-check-hostname", VLC_FALSE, NULL, CHECK_HOSTNAME_TEXT, + CHECK_HOSTNAME_LONGTEXT, VLC_FALSE ); +#endif + add_integer( "dh-bits", DH_BITS, NULL, DH_BITS_TEXT, DH_BITS_LONGTEXT, VLC_TRUE ); add_integer( "tls-cache-expiration", CACHE_EXPIRATION, NULL, @@ -124,7 +152,7 @@ typedef struct tls_client_sys_t static int -_get_Int (vlc_object_t *p_this, const char *var) +_get_Int( vlc_object_t *p_this, const char *var ) { vlc_value_t value; @@ -137,7 +165,22 @@ _get_Int (vlc_object_t *p_this, const char *var) return value.i_int; } +static int +_get_Bool( vlc_object_t *p_this, const char *var ) +{ + vlc_value_t value; + + if( var_Get( p_this, var, &value ) != VLC_SUCCESS ) + { + var_Create( p_this, var, VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Get( p_this, var, &value ); + } + + return value.b_bool; +} + #define get_Int( a, b ) _get_Int( (vlc_object_t *)(a), (b) ) +#define get_Bool( a, b ) _get_Bool( (vlc_object_t *)(a), (b) ) /***************************************************************************** @@ -297,12 +340,81 @@ static void gnutls_ClientDelete( tls_session_t *p_session ) { /* On the client-side, credentials are re-allocated per session */ - gnutls_certificate_free_credentials( ((tls_client_sys_t *) - (p_session->p_sys))->x509_cred ); + gnutls_certificate_credentials x509_cred = + ((tls_client_sys_t *)(p_session->p_sys))->x509_cred; + gnutls_SessionClose( p_session ); + + /* credentials must be free'd *after* gnutls_deinit() */ + gnutls_certificate_free_credentials( x509_cred ); +} + + +inline int +is_regular( const char *psz_filename ) +{ +#ifdef HAVE_SYS_STAT_H + struct stat st; + + return ( stat( psz_filename, &st ) == 0 ) + && S_ISREG( st.st_mode ); +#else + return 1; +#endif } +static int +gnutls_AddCADirectory( vlc_object_t *p_this, + gnutls_certificate_credentials cred, + const char *psz_dirname ) +{ + DIR* dir; + struct dirent *p_ent; + int i_len; + + if( *psz_dirname == '\0' ) + psz_dirname = "."; + + dir = opendir( psz_dirname ); + if( dir == NULL ) + { + msg_Warn( p_this, "Cannot open directory (%s) : %s", psz_dirname, + strerror( errno ) ); + return VLC_EGENERIC; + } + + i_len = strlen( psz_dirname ) + 2; + + while( ( p_ent = readdir( dir ) ) != NULL ) + { + char *psz_filename; + + psz_filename = (char *)malloc( i_len + strlen( p_ent->d_name ) ); + if( psz_filename == NULL ) + return VLC_ENOMEM; + + sprintf( psz_filename, "%s/%s", psz_dirname, p_ent->d_name ); + /* we neglect the race condition here - not security sensitive */ + if( is_regular( psz_filename ) ) + { + int i; + + i = gnutls_certificate_set_x509_trust_file( cred, psz_filename, + GNUTLS_X509_FMT_PEM ); + if( i < 0 ) + { + msg_Warn( p_this, "Cannot add trusted CA (%s) : %s", + psz_filename, gnutls_strerror( i ) ); + } + } + free( psz_filename ); + } + + closedir( dir ); + return VLC_SUCCESS; +} + /***************************************************************************** * tls_ClientCreate: ***************************************************************************** @@ -336,7 +448,6 @@ gnutls_ClientCreate( tls_t *p_tls ) p_session->sock.pf_send = gnutls_Send; p_session->sock.pf_recv = gnutls_Recv; p_session->pf_handshake = gnutls_BeginHandshake; - p_session->pf_handshake2 = gnutls_ContinueHandshake; p_session->pf_close = gnutls_ClientDelete; p_sys->session.b_handshaked = VLC_FALSE; @@ -351,21 +462,30 @@ gnutls_ClientCreate( tls_t *p_tls ) goto error; } -#if 0 - if( psz_ca_path != NULL ) + if( get_Bool( p_tls, "tls-check-cert" ) ) { - i_val = gnutls_certificate_set_x509_trust_file( p_sys->x509_cred, - psz_ca_path, - GNUTLS_X509_FMT_PEM ); - if( i_val != 0 ) + /* FIXME: support for changing path/using multiple paths */ + char *psz_path; + const char *psz_homedir; + + psz_homedir = p_tls->p_vlc->psz_homedir; + psz_path = (char *)malloc( strlen( psz_homedir ) + + sizeof( CONFIG_DIR ) + 5 ); + if( psz_path == NULL ) { - msg_Err( p_tls, "Cannot add trusted CA (%s) : %s", psz_ca_path, - gnutls_strerror( i_val ) ); gnutls_certificate_free_credentials( p_sys->x509_cred ); goto error; } + + sprintf( psz_path, "%s/"CONFIG_DIR"/ssl", psz_homedir ); + gnutls_AddCADirectory( (vlc_object_t *)p_session, p_sys->x509_cred, + psz_path ); + + p_session->pf_handshake2 = gnutls_HandshakeAndValidate; } -#endif + else + p_session->pf_handshake2 = gnutls_ContinueHandshake; + i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT ); if( i_val != 0 ) { @@ -526,6 +646,7 @@ static tls_session_t * gnutls_ServerSessionPrepare( tls_server_t *p_server ) { tls_session_t *p_session; + tls_server_sys_t *p_server_sys; gnutls_session session; int i_val; @@ -542,12 +663,12 @@ gnutls_ServerSessionPrepare( tls_server_t *p_server ) vlc_object_attach( p_session, p_server ); + p_server_sys = (tls_server_sys_t *)p_server->p_sys; p_session->sock.p_sys = p_session; p_session->sock.pf_send = gnutls_Send; p_session->sock.pf_recv = gnutls_Recv; p_session->pf_handshake = gnutls_BeginHandshake; - p_session->pf_handshake2 = ((tls_server_sys_t *) - (p_server->p_sys))->pf_handshake2; + p_session->pf_handshake2 = p_server_sys->pf_handshake2; p_session->pf_close = gnutls_SessionClose; ((tls_session_sys_t *)p_session->p_sys)->b_handshaked = VLC_FALSE; @@ -572,8 +693,7 @@ gnutls_ServerSessionPrepare( tls_server_t *p_server ) } i_val = gnutls_credentials_set( session, GNUTLS_CRD_CERTIFICATE, - ((tls_server_sys_t *)(p_server->p_sys)) - ->x509_cred ); + p_server_sys->x509_cred ); if( i_val < 0 ) { msg_Err( p_server, "Cannot set TLS session credentials : %s", @@ -582,9 +702,8 @@ gnutls_ServerSessionPrepare( tls_server_t *p_server ) goto error; } - /* TODO: support for client authentication */ - /*gnutls_certificate_server_set_request( p_session->session, - GNUTLS_CERT_REQUEST ); */ + if( p_session->pf_handshake2 == gnutls_HandshakeAndValidate ) + gnutls_certificate_server_set_request( session, GNUTLS_CERT_REQUIRE ); gnutls_dh_set_prime_bits( session, get_Int( p_server, "dh-bits" ) ); @@ -615,17 +734,17 @@ static void gnutls_ServerDelete( tls_server_t *p_server ) { tls_server_sys_t *p_sys; - p_sys = (tls_server_sys_t *)p_server->p_sys; - gnutls_certificate_free_credentials( p_sys->x509_cred ); - gnutls_dh_params_deinit( p_sys->dh_params ); vlc_mutex_destroy( &p_sys->cache_lock ); + free( p_sys->p_cache ); vlc_object_detach( p_server ); vlc_object_destroy( p_server ); - free( p_sys->p_cache ); + /* all sessions depending on the server are now deinitialized */ + gnutls_certificate_free_credentials( p_sys->x509_cred ); + gnutls_dh_params_deinit( p_sys->dh_params ); free( p_sys ); } @@ -651,7 +770,6 @@ gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path ) { msg_Err( p_server, "Cannot add trusted CA (%s) : %s", psz_ca_path, gnutls_strerror( val ) ); - gnutls_ServerDelete( p_server ); return VLC_EGENERIC; } msg_Dbg( p_server, " %d trusted CA added (%s)", val, psz_ca_path ); @@ -682,7 +800,6 @@ gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path ) { msg_Err( p_server, "Cannot add CRL (%s) : %s", psz_crl_path, gnutls_strerror( val ) ); - gnutls_ServerDelete( p_server ); return VLC_EGENERIC; } msg_Dbg( p_server, "%d CRL added (%s)", val, psz_crl_path ); @@ -867,6 +984,8 @@ Open( vlc_object_t *p_this ) if( count.i_int == 0) { + const char *psz_version; + __p_gcry_data = VLC_OBJECT( p_this->p_vlc ); gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc); @@ -876,14 +995,19 @@ Open( vlc_object_t *p_this ) vlc_mutex_unlock( lock.p_address ); return VLC_EGENERIC; } - if( gnutls_check_version( "1.0.0" ) == NULL ) + /* + * FIXME: in fact, we currently depends on 1.0.17, but it breaks on + * Debian which as a patched 1.0.16 (which we can use). + */ + psz_version = gnutls_check_version( "1.0.16" ); + if( psz_version == NULL ) { gnutls_global_deinit( ); vlc_mutex_unlock( lock.p_address ); msg_Err( p_this, "unsupported GnuTLS version" ); return VLC_EGENERIC; } - msg_Dbg( p_this, "GnuTLS initialized" ); + msg_Dbg( p_this, "GnuTLS v%s initialized", psz_version ); } count.i_int++;