Commit 42327f35 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

Rework/simplify the TLS plugin interface (LibVLC <-> tls plugin).

Remove the singleton pattern. Still very much work in progress.
parent 0753cb73
...@@ -411,7 +411,6 @@ typedef struct httpd_redirect_t httpd_redirect_t; ...@@ -411,7 +411,6 @@ typedef struct httpd_redirect_t httpd_redirect_t;
typedef struct httpd_stream_t httpd_stream_t; typedef struct httpd_stream_t httpd_stream_t;
/* TLS support */ /* TLS support */
typedef struct tls_t tls_t;
typedef struct tls_server_t tls_server_t; typedef struct tls_server_t tls_server_t;
typedef struct tls_session_t tls_session_t; typedef struct tls_session_t tls_session_t;
......
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
#define VLC_OBJECT_FILTER (-22) #define VLC_OBJECT_FILTER (-22)
#define VLC_OBJECT_VOD (-23) #define VLC_OBJECT_VOD (-23)
#define VLC_OBJECT_SPU (-24) #define VLC_OBJECT_SPU (-24)
#define VLC_OBJECT_TLS (-25) /*#define VLC_OBJECT_xxx (-25) - formerly TLS */
#define VLC_OBJECT_SD (-26) #define VLC_OBJECT_SD (-26)
#define VLC_OBJECT_XML (-27) #define VLC_OBJECT_XML (-27)
#define VLC_OBJECT_OSDMENU (-28) #define VLC_OBJECT_OSDMENU (-28)
......
/***************************************************************************** /*****************************************************************************
* tls.c: TLS wrapper * tls.c: Transport Layer Security API
***************************************************************************** *****************************************************************************
* Copyright (C) 2004-2005 the VideoLAN team * Copyright (C) 2004-2007 the VideoLAN team
* $Id$ * $Id$
* *
* Authors: Rémi Denis-Courmont <rem # videolan.org> * Authors: Rémi Denis-Courmont <rem # videolan.org>
...@@ -30,26 +30,14 @@ ...@@ -30,26 +30,14 @@
# include <vlc_network.h> # include <vlc_network.h>
struct tls_t typedef struct tls_server_sys_t tls_server_sys_t;
{
VLC_COMMON_MEMBERS
/* Module properties */
module_t *p_module;
void *p_sys;
tls_server_t * (*pf_server_create) ( tls_t *, const char *,
const char * );
tls_session_t * (*pf_client_create) ( tls_t * );
};
struct tls_server_t struct tls_server_t
{ {
VLC_COMMON_MEMBERS VLC_COMMON_MEMBERS
void *p_sys; module_t *p_module;
tls_server_sys_t *p_sys;
void (*pf_delete) ( tls_server_t * );
int (*pf_add_CA) ( tls_server_t *, const char * ); int (*pf_add_CA) ( tls_server_t *, const char * );
int (*pf_add_CRL) ( tls_server_t *, const char * ); int (*pf_add_CRL) ( tls_server_t *, const char * );
...@@ -57,11 +45,14 @@ struct tls_server_t ...@@ -57,11 +45,14 @@ struct tls_server_t
tls_session_t * (*pf_session_prepare) ( tls_server_t * ); tls_session_t * (*pf_session_prepare) ( tls_server_t * );
}; };
typedef struct tls_session_sys_t tls_session_sys_t;
struct tls_session_t struct tls_session_t
{ {
VLC_COMMON_MEMBERS VLC_COMMON_MEMBERS
void *p_sys; module_t *p_module;
tls_session_sys_t *p_sys;
struct virtual_socket_t sock; struct virtual_socket_t sock;
int (*pf_handshake) ( tls_session_t *, int, const char * ); int (*pf_handshake) ( tls_session_t *, int, const char * );
......
...@@ -49,15 +49,17 @@ ...@@ -49,15 +49,17 @@
#include <gnutls/gnutls.h> #include <gnutls/gnutls.h>
#include <gnutls/x509.h> #include <gnutls/x509.h>
#define DH_BITS 1024 #define DH_BITS 1024
#define CACHE_EXPIRATION 3600 #define CACHE_TIMEOUT 3600
#define CACHE_SIZE 64 #define CACHE_SIZE 64
/***************************************************************************** /*****************************************************************************
* Module descriptor * Module descriptor
*****************************************************************************/ *****************************************************************************/
static int Open ( vlc_object_t * ); static int OpenClient (vlc_object_t *);
static void Close( vlc_object_t * ); static void CloseClient (vlc_object_t *);
static int OpenServer (vlc_object_t *);
static void CloseServer (vlc_object_t *);
#define DH_BITS_TEXT N_("Diffie-Hellman prime bits") #define DH_BITS_TEXT N_("Diffie-Hellman prime bits")
#define DH_BITS_LONGTEXT N_( \ #define DH_BITS_LONGTEXT N_( \
...@@ -65,8 +67,8 @@ static void Close( vlc_object_t * ); ...@@ -65,8 +67,8 @@ static void Close( vlc_object_t * );
"used for TLS or SSL-based server-side encryption. This is generally " \ "used for TLS or SSL-based server-side encryption. This is generally " \
"not needed." ) "not needed." )
#define CACHE_EXPIRATION_TEXT N_("Expiration time for resumed TLS sessions") #define CACHE_TIMEOUT_TEXT N_("Expiration time for resumed TLS sessions")
#define CACHE_EXPIRATION_LONGTEXT N_( \ #define CACHE_TIMEOUT_LONGTEXT N_( \
"It is possible to cache the resumed TLS sessions. This is the expiration "\ "It is possible to cache the resumed TLS sessions. This is the expiration "\
"time of the sessions stored in this cache, in seconds." ) "time of the sessions stored in this cache, in seconds." )
...@@ -82,9 +84,9 @@ static void Close( vlc_object_t * ); ...@@ -82,9 +84,9 @@ static void Close( vlc_object_t * );
vlc_module_begin(); vlc_module_begin();
set_shortname( "GnuTLS" ); set_shortname( "GnuTLS" );
set_description( _("GnuTLS TLS encryption layer") ); set_description( _("GnuTLS transport layer security") );
set_capability( "tls", 1 ); set_capability( "tls client", 1 );
set_callbacks( Open, Close ); set_callbacks( OpenClient, CloseClient );
set_category( CAT_ADVANCED ); set_category( CAT_ADVANCED );
set_subcategory( SUBCAT_ADVANCED_MISC ); set_subcategory( SUBCAT_ADVANCED_MISC );
...@@ -92,55 +94,135 @@ vlc_module_begin(); ...@@ -92,55 +94,135 @@ vlc_module_begin();
CHECK_CERT_LONGTEXT, VLC_FALSE ); CHECK_CERT_LONGTEXT, VLC_FALSE );
add_obsolete_bool( "tls-check-hostname" ); add_obsolete_bool( "tls-check-hostname" );
add_integer( "gnutls-dh-bits", DH_BITS, NULL, DH_BITS_TEXT, add_submodule();
DH_BITS_LONGTEXT, VLC_TRUE ); set_description( _("GnuTLS server") );
add_integer( "gnutls-cache-expiration", CACHE_EXPIRATION, NULL, set_capability( "tls server", 1 );
CACHE_EXPIRATION_TEXT, CACHE_EXPIRATION_LONGTEXT, VLC_TRUE ); set_category( CAT_ADVANCED );
add_integer( "gnutls-cache-size", CACHE_SIZE, NULL, CACHE_SIZE_TEXT, set_subcategory( SUBCAT_ADVANCED_MISC );
CACHE_SIZE_LONGTEXT, VLC_TRUE ); set_callbacks( OpenServer, CloseServer );
add_integer( "gnutls-dh-bits", DH_BITS, NULL, DH_BITS_TEXT,
DH_BITS_LONGTEXT, VLC_TRUE );
add_integer( "gnutls-cache-timeout", CACHE_TIMEOUT, NULL,
CACHE_TIMEOUT_TEXT, CACHE_TIMEOUT_LONGTEXT, VLC_TRUE );
add_integer( "gnutls-cache-size", CACHE_SIZE, NULL, CACHE_SIZE_TEXT,
CACHE_SIZE_LONGTEXT, VLC_TRUE );
vlc_module_end(); vlc_module_end();
#define MAX_SESSION_ID 32
#define MAX_SESSION_DATA 1024
typedef struct saved_session_t #ifdef LIBVLC_USE_PTHREAD
GCRY_THREAD_OPTION_PTHREAD_IMPL;
# define gcry_threads_vlc gcry_threads_pthread
#else
/**
* gcrypt thread option VLC implementation
*/
# define NEED_THREAD_CONTEXT 1
static vlc_object_t *__p_gcry_data = NULL;
static int gcry_vlc_mutex_init( void **p_sys )
{ {
char id[MAX_SESSION_ID]; int i_val;
char data[MAX_SESSION_DATA]; vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc( sizeof( vlc_mutex_t ) );
unsigned i_idlen; if( p_lock == NULL)
unsigned i_datalen; return ENOMEM;
} saved_session_t;
i_val = vlc_mutex_init( __p_gcry_data, p_lock );
if( i_val )
free( p_lock );
else
*p_sys = p_lock;
return i_val;
}
typedef struct tls_server_sys_t static int gcry_vlc_mutex_destroy( void **p_sys )
{ {
gnutls_certificate_credentials x509_cred; int i_val;
gnutls_dh_params dh_params; vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys;
struct saved_session_t *p_cache; i_val = vlc_mutex_destroy( p_lock );
struct saved_session_t *p_store; free( p_lock );
int i_cache_size; return i_val;
vlc_mutex_t cache_lock; }
int (*pf_handshake2)( tls_session_t * ); static int gcry_vlc_mutex_lock( void **p_sys )
} tls_server_sys_t; {
return vlc_mutex_lock( (vlc_mutex_t *)*p_sys );
}
static int gcry_vlc_mutex_unlock( void **lock )
{
return vlc_mutex_unlock( (vlc_mutex_t *)*lock );
}
typedef struct tls_session_sys_t static struct gcry_thread_cbs gcry_threads_vlc =
{ {
gnutls_session session; GCRY_THREAD_OPTION_USER,
char *psz_hostname; NULL,
vlc_bool_t b_handshaked; gcry_vlc_mutex_init,
} tls_session_sys_t; gcry_vlc_mutex_destroy,
gcry_vlc_mutex_lock,
gcry_vlc_mutex_unlock
};
#endif
typedef struct tls_client_sys_t /**
* Initializes GnuTLS with proper locking.
* @return VLC_SUCCESS on success, a VLC error code otherwise.
*/
static int gnutls_Init (vlc_object_t *p_this)
{ {
struct tls_session_sys_t session; int ret = VLC_EGENERIC;
gnutls_certificate_credentials x509_cred;
} tls_client_sys_t; vlc_mutex_t *lock = var_GetGlobalMutex ("gnutls_mutex");
vlc_mutex_lock (lock);
/* This should probably be removed/fixed. It will screw up with multiple
* LibVLC instances. */
#ifdef NEED_THREAD_CONTEXT
__p_gcry_data = VLC_OBJECT (p_this->p_libvlc);
#endif
gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc);
if (gnutls_global_init ())
{
msg_Err (p_this, "cannot initialize GnuTLS");
goto error;
}
const char *psz_version = gnutls_check_version ("1.3.3");
if (psz_version == NULL)
{
msg_Err (p_this, "unsupported GnuTLS version");
gnutls_global_deinit ();
goto error;
}
msg_Dbg (p_this, "GnuTLS v%s initialized", psz_version);
ret = VLC_SUCCESS;
error:
vlc_mutex_unlock (lock);
return ret;
}
/**
* Deinitializes GnuTLS.
*/
static void gnutls_Deinit (vlc_object_t *p_this)
{
vlc_mutex_t *lock = var_GetGlobalMutex( "gnutls_mutex" );
vlc_mutex_lock (lock);
gnutls_global_deinit ();
msg_Dbg (p_this, "GnuTLS deinitialized");
vlc_mutex_unlock (lock);
}
static int gnutls_Error (vlc_object_t *obj, int val) static int gnutls_Error (vlc_object_t *obj, int val)
...@@ -148,15 +230,15 @@ static int gnutls_Error (vlc_object_t *obj, int val) ...@@ -148,15 +230,15 @@ static int gnutls_Error (vlc_object_t *obj, int val)
switch (val) switch (val)
{ {
case GNUTLS_E_AGAIN: case GNUTLS_E_AGAIN:
#if ! defined(WIN32) #ifndef WIN32
errno = EAGAIN; errno = EAGAIN;
break; break;
#endif #endif
/* WinSock does not return EAGAIN, return EINTR instead */ /* WinSock does not return EAGAIN, return EINTR instead */
case GNUTLS_E_INTERRUPTED: case GNUTLS_E_INTERRUPTED:
#if defined(WIN32) #ifdef WIN32
WSASetLastError(WSAEINTR); WSASetLastError (WSAEINTR);
#else #else
errno = EINTR; errno = EINTR;
#endif #endif
...@@ -164,12 +246,12 @@ static int gnutls_Error (vlc_object_t *obj, int val) ...@@ -164,12 +246,12 @@ static int gnutls_Error (vlc_object_t *obj, int val)
default: default:
msg_Err (obj, "%s", gnutls_strerror (val)); msg_Err (obj, "%s", gnutls_strerror (val));
#ifdef DEBUG #ifndef NDEBUG
if (!gnutls_error_is_fatal (val)) if (!gnutls_error_is_fatal (val))
msg_Err (obj, "Error above should be handled"); msg_Err (obj, "Error above should be handled");
#endif #endif
#if defined(WIN32) #ifdef WIN32
WSASetLastError(WSAECONNRESET); WSASetLastError (WSAECONNRESET);
#else #else
errno = ECONNRESET; errno = ECONNRESET;
#endif #endif
...@@ -178,6 +260,14 @@ static int gnutls_Error (vlc_object_t *obj, int val) ...@@ -178,6 +260,14 @@ static int gnutls_Error (vlc_object_t *obj, int val)
} }
struct tls_session_sys_t
{
gnutls_session session;
char *psz_hostname;
vlc_bool_t b_handshaked;
};
/** /**
* Sends data through a TLS session. * Sends data through a TLS session.
*/ */
...@@ -210,14 +300,11 @@ gnutls_Recv( void *p_session, void *buf, int i_length ) ...@@ -210,14 +300,11 @@ gnutls_Recv( void *p_session, void *buf, int i_length )
} }
/***************************************************************************** /**
* tls_Session(Continue)?Handshake: * @return -1 on error (you need not and must not call tls_SessionClose())
*****************************************************************************
* Establishes TLS session with a peer through socket <fd>.
* Returns -1 on error (you need not and must not call tls_SessionClose)
* 0 on succesful handshake completion, 1 if more would-be blocking recv is * 0 on succesful handshake completion, 1 if more would-be blocking recv is
* needed, 2 if more would-be blocking send is required. * needed, 2 if more would-be blocking send is required.
*****************************************************************************/ */
static int static int
gnutls_ContinueHandshake( tls_session_t *p_session) gnutls_ContinueHandshake( tls_session_t *p_session)
{ {
...@@ -385,9 +472,7 @@ static int ...@@ -385,9 +472,7 @@ static int
gnutls_BeginHandshake( tls_session_t *p_session, int fd, gnutls_BeginHandshake( tls_session_t *p_session, int fd,
const char *psz_hostname ) const char *psz_hostname )
{ {
tls_session_sys_t *p_sys; tls_session_sys_t *p_sys = p_session->p_sys;
p_sys = (tls_session_sys_t *)(p_session->p_sys);
gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)(intptr_t)fd); gnutls_transport_set_ptr (p_sys->session, (gnutls_transport_ptr)(intptr_t)fd);
...@@ -406,31 +491,6 @@ gnutls_BeginHandshake( tls_session_t *p_session, int fd, ...@@ -406,31 +491,6 @@ gnutls_BeginHandshake( tls_session_t *p_session, int fd,
return p_session->pf_handshake2( p_session ); return p_session->pf_handshake2( p_session );
} }
/**
* Terminates TLS session and releases session data.
* You still have to close the socket yourself.
*/
static void
gnutls_SessionClose( tls_session_t *p_session )
{
tls_session_sys_t *p_sys;
p_sys = (tls_session_sys_t *)(p_session->p_sys);
if( p_sys->b_handshaked == VLC_TRUE )
gnutls_bye( p_sys->session, GNUTLS_SHUT_WR );
gnutls_deinit( p_sys->session );
if( p_sys->psz_hostname != NULL )
free( p_sys->psz_hostname );
vlc_object_detach( p_session );
vlc_object_destroy( p_session );
free( p_sys );
}
typedef int (*tls_prio_func) (gnutls_session_t, const int *); typedef int (*tls_prio_func) (gnutls_session_t, const int *);
static int static int
...@@ -535,20 +595,6 @@ gnutls_SessionPrioritize (vlc_object_t *obj, gnutls_session_t session) ...@@ -535,20 +595,6 @@ gnutls_SessionPrioritize (vlc_object_t *obj, gnutls_session_t session)
} }
static void
gnutls_ClientDelete( tls_session_t *p_session )
{
/* On the client-side, credentials are re-allocated per session */
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 );
}
static int static int
gnutls_Addx509File( vlc_object_t *p_this, gnutls_Addx509File( vlc_object_t *p_this,
gnutls_certificate_credentials cred, gnutls_certificate_credentials cred,
...@@ -659,62 +705,65 @@ gnutls_Addx509File( vlc_object_t *p_this, ...@@ -659,62 +705,65 @@ gnutls_Addx509File( vlc_object_t *p_this,
} }
/** TLS client session data */
typedef struct tls_client_sys_t
{
struct tls_session_sys_t session;
gnutls_certificate_credentials x509_cred;
} tls_client_sys_t;
/** /**
* Initializes a client-side TLS session. * Initializes a client-side TLS session.
*/ */
static tls_session_t * static int OpenClient (vlc_object_t *obj)
gnutls_ClientCreate( tls_t *p_tls )
{ {
tls_session_t *p_session = NULL; tls_session_t *p_session = (tls_session_t *)obj;
tls_client_sys_t *p_sys = NULL;
int i_val; int i_val;
p_sys = (tls_client_sys_t *)malloc( sizeof(struct tls_client_sys_t) ); if (gnutls_Init (obj))
if( p_sys == NULL ) return VLC_EGENERIC;
return NULL;
p_session = (struct tls_session_t *)vlc_object_create ( p_tls, sizeof(struct tls_session_t) ); tls_client_sys_t *p_sys = malloc (sizeof (*p_sys));
if( p_session == NULL ) if (p_sys == NULL)
{ {
free( p_sys ); gnutls_Deinit (obj);
return NULL; return VLC_ENOMEM;
} }
p_session->p_sys = p_sys; p_session->p_sys = &p_sys->session;
p_session->sock.p_sys = p_session; p_session->sock.p_sys = p_session;
p_session->sock.pf_send = gnutls_Send; p_session->sock.pf_send = gnutls_Send;
p_session->sock.pf_recv = gnutls_Recv; p_session->sock.pf_recv = gnutls_Recv;
p_session->pf_handshake = gnutls_BeginHandshake; p_session->pf_handshake = gnutls_BeginHandshake;
p_session->pf_close = gnutls_ClientDelete; p_session->pf_close = NULL;
p_sys->session.b_handshaked = VLC_FALSE; p_sys->session.b_handshaked = VLC_FALSE;
p_sys->session.psz_hostname = NULL; p_sys->session.psz_hostname = NULL;
vlc_object_attach( p_session, p_tls ); const char *homedir = obj->p_libvlc->psz_datadir,
const char *homedir = p_tls->p_libvlc->psz_datadir,
*datadir = config_GetDataDir (); *datadir = config_GetDataDir ();
size_t l1 = strlen (homedir), l2 = strlen (datadir); size_t l1 = strlen (homedir), l2 = strlen (datadir);
char path[((l1 > l2) ? l1 : l2) + sizeof ("/ssl/private")]; char path[((l1 > l2) ? l1 : l2) + sizeof ("/ssl/private")];
// > sizeof ("/ssl/certs") // > sizeof ("/ssl/certs")
// > sizeof ("/ca-certificates.crt") // > sizeof ("/ca-certificates.crt")
i_val = gnutls_certificate_allocate_credentials( &p_sys->x509_cred ); i_val = gnutls_certificate_allocate_credentials (&p_sys->x509_cred);
if( i_val != 0 ) if (i_val != 0)
{ {
msg_Err( p_tls, "cannot allocate X509 credentials: %s", msg_Err (obj, "cannot allocate X509 credentials: %s",
gnutls_strerror( i_val ) ); gnutls_strerror (i_val));
goto error; goto error;
} }
if (var_CreateGetBool (p_tls, "tls-check-cert")) if (var_CreateGetBool (obj, "tls-check-cert"))
{ {
sprintf (path, "%s/ssl/certs", homedir); sprintf (path, "%s/ssl/certs", homedir);
gnutls_Addx509Directory ((vlc_object_t *)p_session, gnutls_Addx509Directory (VLC_OBJECT (p_session),
p_sys->x509_cred, path, VLC_FALSE); p_sys->x509_cred, path, VLC_FALSE);
sprintf (path, "%s/ca-certificates.crt", datadir); sprintf (path, "%s/ca-certificates.crt", datadir);
gnutls_Addx509File ((vlc_object_t *)p_session, gnutls_Addx509File (VLC_OBJECT (p_session),
p_sys->x509_cred, path, VLC_FALSE); p_sys->x509_cred, path, VLC_FALSE);
p_session->pf_handshake2 = gnutls_HandshakeAndValidate; p_session->pf_handshake2 = gnutls_HandshakeAndValidate;
} }
...@@ -722,15 +771,15 @@ gnutls_ClientCreate( tls_t *p_tls ) ...@@ -722,15 +771,15 @@ gnutls_ClientCreate( tls_t *p_tls )
p_session->pf_handshake2 = gnutls_ContinueHandshake; p_session->pf_handshake2 = gnutls_ContinueHandshake;
sprintf (path, "%s/ssl/private", homedir); sprintf (path, "%s/ssl/private", homedir);
gnutls_Addx509Directory ((vlc_object_t *)p_session, p_sys->x509_cred, gnutls_Addx509Directory (VLC_OBJECT (p_session), p_sys->x509_cred,
path, VLC_TRUE); path, VLC_TRUE);
i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT ); i_val = gnutls_init( &p_sys->session.session, GNUTLS_CLIENT );
if( i_val != 0 ) if (i_val != 0)
{ {
msg_Err( p_tls, "cannot initialize TLS session: %s", msg_Err (obj, "cannot initialize TLS session: %s",
gnutls_strerror( i_val ) ); gnutls_strerror (i_val));
gnutls_certificate_free_credentials( p_sys->x509_cred ); gnutls_certificate_free_credentials (p_sys->x509_cred);
goto error; goto error;
} }
...@@ -738,34 +787,78 @@ gnutls_ClientCreate( tls_t *p_tls ) ...@@ -738,34 +787,78 @@ gnutls_ClientCreate( tls_t *p_tls )
p_sys->session.session)) p_sys->session.session))
goto s_error; goto s_error;
i_val = gnutls_credentials_set( p_sys->session.session, i_val = gnutls_credentials_set (p_sys->session.session,
GNUTLS_CRD_CERTIFICATE, GNUTLS_CRD_CERTIFICATE,
p_sys->x509_cred ); p_sys->x509_cred);
if( i_val < 0 ) if (i_val < 0)
{ {
msg_Err( p_tls, "cannot set TLS session credentials: %s", msg_Err (obj, "cannot set TLS session credentials: %s",
gnutls_strerror( i_val ) ); gnutls_strerror (i_val));
goto s_error; goto s_error;
} }
return p_session; return VLC_SUCCESS;
s_error: s_error:
gnutls_deinit( p_sys->session.session ); gnutls_deinit (p_sys->session.session);
gnutls_certificate_free_credentials( p_sys->x509_cred ); gnutls_certificate_free_credentials (p_sys->x509_cred);
error: error:
vlc_object_detach( p_session ); gnutls_Deinit (obj);
vlc_object_destroy( p_session ); free (p_sys);
free( p_sys ); return VLC_EGENERIC;
}
return NULL;
static void CloseClient (vlc_object_t *obj)
{
tls_session_t *client = (tls_session_t *)obj;
tls_client_sys_t *p_sys = (tls_client_sys_t *)(client->p_sys);
if (p_sys->session.b_handshaked == VLC_TRUE)
gnutls_bye (p_sys->session.session, GNUTLS_SHUT_WR);
gnutls_deinit (p_sys->session.session);
/* credentials must be free'd *after* gnutls_deinit() */
gnutls_certificate_free_credentials (p_sys->x509_cred);
gnutls_Deinit (obj);
free (p_sys->session.psz_hostname);
free (p_sys);
} }
/**
* Server-side TLS
*/
struct tls_server_sys_t
{
gnutls_certificate_credentials x509_cred;
gnutls_dh_params dh_params;
struct saved_session_t *p_cache;
struct saved_session_t *p_store;
int i_cache_size;
vlc_mutex_t cache_lock;
int (*pf_handshake2)( tls_session_t * );
};
/** /**
* TLS session resumption callbacks (server-side) * TLS session resumption callbacks (server-side)
*/ */
#define MAX_SESSION_ID 32
#define MAX_SESSION_DATA 1024
typedef struct saved_session_t
{
char id[MAX_SESSION_ID];
char data[MAX_SESSION_DATA];
unsigned i_idlen;
unsigned i_datalen;
} saved_session_t;
static int cb_store( void *p_server, gnutls_datum key, gnutls_datum data ) static int cb_store( void *p_server, gnutls_datum key, gnutls_datum data )
{ {
tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys; tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
...@@ -792,10 +885,9 @@ static int cb_store( void *p_server, gnutls_datum key, gnutls_datum data ) ...@@ -792,10 +885,9 @@ static int cb_store( void *p_server, gnutls_datum key, gnutls_datum data )
} }
static const gnutls_datum err_datum = { NULL, 0 };
static gnutls_datum cb_fetch( void *p_server, gnutls_datum key ) static gnutls_datum cb_fetch( void *p_server, gnutls_datum key )
{ {
static const gnutls_datum err_datum = { NULL, 0 };
tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys; tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
saved_session_t *p_session, *p_end; saved_session_t *p_session, *p_end;
...@@ -861,6 +953,26 @@ static int cb_delete( void *p_server, gnutls_datum key ) ...@@ -861,6 +953,26 @@ static int cb_delete( void *p_server, gnutls_datum key )
} }
/**
* Terminates TLS session and releases session data.
* You still have to close the socket yourself.
*/
static void
gnutls_SessionClose( tls_session_t *p_session )
{
tls_session_sys_t *p_sys = p_session->p_sys;
if( p_sys->b_handshaked == VLC_TRUE )
gnutls_bye( p_sys->session, GNUTLS_SHUT_WR );
gnutls_deinit( p_sys->session );
vlc_object_detach( p_session );
vlc_object_destroy( p_session );
free( p_sys );
}
/** /**
* Initializes a server-side TLS session. * Initializes a server-side TLS session.
*/ */
...@@ -885,7 +997,7 @@ gnutls_ServerSessionPrepare( tls_server_t *p_server ) ...@@ -885,7 +997,7 @@ gnutls_ServerSessionPrepare( tls_server_t *p_server )
vlc_object_attach( p_session, p_server ); vlc_object_attach( p_session, p_server );
p_server_sys = (tls_server_sys_t *)p_server->p_sys; p_server_sys = p_server->p_sys;
p_session->sock.p_sys = p_session; p_session->sock.p_sys = p_session;
p_session->sock.pf_send = gnutls_Send; p_session->sock.pf_send = gnutls_Send;
p_session->sock.pf_recv = gnutls_Recv; p_session->sock.pf_recv = gnutls_Recv;
...@@ -893,8 +1005,8 @@ gnutls_ServerSessionPrepare( tls_server_t *p_server ) ...@@ -893,8 +1005,8 @@ gnutls_ServerSessionPrepare( tls_server_t *p_server )
p_session->pf_handshake2 = p_server_sys->pf_handshake2; p_session->pf_handshake2 = p_server_sys->pf_handshake2;
p_session->pf_close = gnutls_SessionClose; p_session->pf_close = gnutls_SessionClose;
((tls_session_sys_t *)p_session->p_sys)->b_handshaked = VLC_FALSE; p_session->p_sys->b_handshaked = VLC_FALSE;
((tls_session_sys_t *)p_session->p_sys)->psz_hostname = NULL; p_session->p_sys->psz_hostname = NULL;
i_val = gnutls_init( &session, GNUTLS_SERVER ); i_val = gnutls_init( &session, GNUTLS_SERVER );
if( i_val != 0 ) if( i_val != 0 )
...@@ -904,7 +1016,7 @@ gnutls_ServerSessionPrepare( tls_server_t *p_server ) ...@@ -904,7 +1016,7 @@ gnutls_ServerSessionPrepare( tls_server_t *p_server )
goto error; goto error;
} }
((tls_session_sys_t *)p_session->p_sys)->session = session; p_session->p_sys->session = session;
if (gnutls_SessionPrioritize (VLC_OBJECT (p_session), session)) if (gnutls_SessionPrioritize (VLC_OBJECT (p_session), session))
{ {
...@@ -946,35 +1058,13 @@ error: ...@@ -946,35 +1058,13 @@ error:
} }
/**
* Releases data allocated with tls_ServerCreate().
*/
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;
vlc_mutex_destroy( &p_sys->cache_lock );
free( p_sys->p_cache );
vlc_object_detach( p_server );
vlc_object_destroy( p_server );
/* 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 );
}
/** /**
* Adds one or more certificate authorities. * Adds one or more certificate authorities.
* *
* @param psz_ca_path (Unicode) path to an x509 certificates list. * @param psz_ca_path (Unicode) path to an x509 certificates list.
* *
* @return -1 on error, 0 on success. * @return -1 on error, 0 on success.
*****************************************************************************/ */
static int static int
gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path ) gnutls_ServerAddCA( tls_server_t *p_server, const char *psz_ca_path )
{ {
...@@ -1035,46 +1125,33 @@ gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path ) ...@@ -1035,46 +1125,33 @@ gnutls_ServerAddCRL( tls_server_t *p_server, const char *psz_crl_path )
/** /**
* Allocates a whole server's TLS credentials. * Allocates a whole server's TLS credentials.
*
* @return NULL on error.
*/ */
static tls_server_t * static int OpenServer (vlc_object_t *obj)
gnutls_ServerCreate( tls_t *p_tls, const char *psz_cert_path,
const char *psz_key_path )
{ {
tls_server_t *p_server; tls_server_t *p_server = (tls_server_t *)obj;
tls_server_sys_t *p_sys; tls_server_sys_t *p_sys;
char *psz_local_key, *psz_local_cert;
int val; int val;
msg_Dbg( p_tls, "creating TLS server" ); if (gnutls_Init (obj))
return VLC_EGENERIC;
msg_Dbg (obj, "creating TLS server");
p_sys = (tls_server_sys_t *)malloc( sizeof(struct tls_server_sys_t) ); p_sys = (tls_server_sys_t *)malloc( sizeof(struct tls_server_sys_t) );
if( p_sys == NULL ) if( p_sys == NULL )
return NULL; return VLC_ENOMEM;
p_sys->i_cache_size = config_GetInt (p_tls, "gnutls-cache-size"); p_sys->i_cache_size = config_GetInt (obj, "gnutls-cache-size");
p_sys->p_cache = (struct saved_session_t *)calloc( p_sys->i_cache_size, p_sys->p_cache = calloc (p_sys->i_cache_size,
sizeof( struct saved_session_t ) ); sizeof (struct saved_session_t));
if( p_sys->p_cache == NULL ) if (p_sys->p_cache == NULL)
{ {
free( p_sys ); free (p_sys);
return NULL; return VLC_ENOMEM;
} }
p_sys->p_store = p_sys->p_cache;
p_server = vlc_object_create( p_tls, sizeof(struct tls_server_t) );
if( p_server == NULL )
{
free( p_sys->p_cache );
free( p_sys );
return NULL;
}
vlc_object_attach( p_server, p_tls );
p_sys->p_store = p_sys->p_cache;
p_server->p_sys = p_sys; p_server->p_sys = p_sys;
p_server->pf_delete = gnutls_ServerDelete;
p_server->pf_add_CA = gnutls_ServerAddCA; p_server->pf_add_CA = gnutls_ServerAddCA;
p_server->pf_add_CRL = gnutls_ServerAddCRL; p_server->pf_add_CRL = gnutls_ServerAddCRL;
p_server->pf_session_prepare = gnutls_ServerSessionPrepare; p_server->pf_session_prepare = gnutls_ServerSessionPrepare;
...@@ -1093,13 +1170,18 @@ gnutls_ServerCreate( tls_t *p_tls, const char *psz_cert_path, ...@@ -1093,13 +1170,18 @@ gnutls_ServerCreate( tls_t *p_tls, const char *psz_cert_path,
goto error; goto error;
} }
psz_local_cert = ToLocale( psz_cert_path ); char *psz_cert_path = var_GetNonEmptyString (obj, "tls-x509-cert");
psz_local_key = ToLocale( psz_key_path ); char *psz_key_path = var_GetNonEmptyString (obj, "tls-x509-key");
val = gnutls_certificate_set_x509_key_file( p_sys->x509_cred, const char *psz_local_cert = ToLocale (psz_cert_path);
const char *psz_local_key = ToLocale (psz_key_path);
val = gnutls_certificate_set_x509_key_file (p_sys->x509_cred,
psz_local_cert, psz_local_key, psz_local_cert, psz_local_key,
GNUTLS_X509_FMT_PEM ); GNUTLS_X509_FMT_PEM );
LocaleFree( psz_cert_path ); LocaleFree (psz_key_path);
LocaleFree( psz_key_path ); free (psz_key_path);
LocaleFree (psz_cert_path);
free (psz_cert_path);
if( val < 0 ) if( val < 0 )
{ {
msg_Err( p_server, "cannot set certificate chain or private key: %s", msg_Err( p_server, "cannot set certificate chain or private key: %s",
...@@ -1116,8 +1198,8 @@ gnutls_ServerCreate( tls_t *p_tls, const char *psz_cert_path, ...@@ -1116,8 +1198,8 @@ gnutls_ServerCreate( tls_t *p_tls, const char *psz_cert_path,
if( val >= 0 ) if( val >= 0 )
{ {
msg_Dbg( p_server, "computing Diffie Hellman ciphers parameters" ); msg_Dbg( p_server, "computing Diffie Hellman ciphers parameters" );
val = gnutls_dh_params_generate2( p_sys->dh_params, val = gnutls_dh_params_generate2 (p_sys->dh_params,
config_GetInt( p_tls, "gnutls-dh-bits" ) ); config_GetInt (obj, "gnutls-dh-bits"));
} }
if( val < 0 ) if( val < 0 )
{ {
...@@ -1130,144 +1212,29 @@ gnutls_ServerCreate( tls_t *p_tls, const char *psz_cert_path, ...@@ -1130,144 +1212,29 @@ gnutls_ServerCreate( tls_t *p_tls, const char *psz_cert_path,
gnutls_certificate_set_dh_params( p_sys->x509_cred, p_sys->dh_params); gnutls_certificate_set_dh_params( p_sys->x509_cred, p_sys->dh_params);
return p_server; return VLC_SUCCESS;
error: error:
vlc_mutex_destroy( &p_sys->cache_lock ); vlc_mutex_destroy (&p_sys->cache_lock);
vlc_object_detach( p_server ); free (p_sys->p_cache);
vlc_object_destroy( p_server ); free (p_sys);
free( p_sys ); return VLC_EGENERIC;
return NULL;
} }
#ifdef LIBVLC_USE_PTHREAD
GCRY_THREAD_OPTION_PTHREAD_IMPL;
# define gcry_threads_vlc gcry_threads_pthread
#else
/** /**
* gcrypt thread option VLC implementation * Destroys a TLS server object.
*/ */
static void CloseServer (vlc_object_t *p_server)
# define NEED_THREAD_CONTEXT 1
static vlc_object_t *__p_gcry_data;
static int gcry_vlc_mutex_init( void **p_sys )
{
int i_val;
vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc( sizeof( vlc_mutex_t ) );
if( p_lock == NULL)
return ENOMEM;
i_val = vlc_mutex_init( __p_gcry_data, p_lock );
if( i_val )
free( p_lock );
else
*p_sys = p_lock;
return i_val;
}
static int gcry_vlc_mutex_destroy( void **p_sys )
{
int i_val;
vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys;
i_val = vlc_mutex_destroy( p_lock );
free( p_lock );
return i_val;
}
static int gcry_vlc_mutex_lock( void **p_sys )
{
return vlc_mutex_lock( (vlc_mutex_t *)*p_sys );
}
static int gcry_vlc_mutex_unlock( void **lock )
{
return vlc_mutex_unlock( (vlc_mutex_t *)*lock );
}
static struct gcry_thread_cbs gcry_threads_vlc =
{
GCRY_THREAD_OPTION_USER,
NULL,
gcry_vlc_mutex_init,
gcry_vlc_mutex_destroy,
gcry_vlc_mutex_lock,
gcry_vlc_mutex_unlock
};
#endif
/*****************************************************************************
* Module initialization
*****************************************************************************/
static unsigned refs = 0;
static int
Open( vlc_object_t *p_this )
{ {
tls_t *p_tls = (tls_t *)p_this; tls_server_sys_t *p_sys = ((tls_server_t *)p_server)->p_sys;
vlc_mutex_t *lock;
lock = var_GetGlobalMutex( "gnutls_mutex" );
vlc_mutex_lock( lock );
/* Initialize GnuTLS only once */
if( refs == 0 )
{
#ifdef NEED_THREAD_CONTEXT
__p_gcry_data = VLC_OBJECT( p_this->p_libvlc );
#endif
gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc);
if( gnutls_global_init( ) )
{
msg_Warn( p_this, "cannot initialize GnuTLS" );
vlc_mutex_unlock( lock );
return VLC_EGENERIC;
}
const char *psz_version = gnutls_check_version( "1.2.9" );
if( psz_version == NULL )
{
gnutls_global_deinit( );
vlc_mutex_unlock( lock );
msg_Err( p_this, "unsupported GnuTLS version" );
return VLC_EGENERIC;
}
msg_Dbg( p_this, "GnuTLS v%s initialized", psz_version );
}
refs++;
vlc_mutex_unlock( lock );
p_tls->pf_server_create = gnutls_ServerCreate;
p_tls->pf_client_create = gnutls_ClientCreate;
return VLC_SUCCESS;
}
/*****************************************************************************
* Module deinitialization
*****************************************************************************/
static void
Close( vlc_object_t *p_this )
{
/*tls_t *p_tls = (tls_t *)p_this;
tls_sys_t *p_sys = (tls_sys_t *)(p_this->p_sys);*/
vlc_mutex_t *lock;
lock = var_GetGlobalMutex( "gnutls_mutex" ); vlc_mutex_destroy (&p_sys->cache_lock);
vlc_mutex_lock( lock ); free (p_sys->p_cache);
if( --refs == 0 ) /* all sessions depending on the server are now deinitialized */
{ gnutls_certificate_free_credentials (p_sys->x509_cred);
gnutls_global_deinit( ); gnutls_dh_params_deinit (p_sys->dh_params);
msg_Dbg( p_this, "GnuTLS deinitialized" ); free (p_sys);
}
vlc_mutex_unlock( lock ); gnutls_Deinit (p_server);
} }
...@@ -299,10 +299,6 @@ void * __vlc_object_create( vlc_object_t *p_this, int i_type ) ...@@ -299,10 +299,6 @@ void * __vlc_object_create( vlc_object_t *p_this, int i_type )
i_size = sizeof( vod_t ); i_size = sizeof( vod_t );
psz_type = "vod server"; psz_type = "vod server";
break; break;
case VLC_OBJECT_TLS:
i_size = sizeof( tls_t );
psz_type = "tls";
break;
case VLC_OBJECT_XML: case VLC_OBJECT_XML:
i_size = sizeof( xml_t ); i_size = sizeof( xml_t );
psz_type = "xml"; psz_type = "xml";
......
...@@ -27,121 +27,70 @@ ...@@ -27,121 +27,70 @@
*/ */
#include <vlc/vlc.h> #include <vlc/vlc.h>
#include "libvlc.h"
#include <vlc_tls.h> #include <vlc_tls.h>
static tls_t *
tls_Init( vlc_object_t *p_this )
{
tls_t *p_tls;
vlc_value_t lockval;
var_Create( p_this->p_libvlc, "tls_mutex", VLC_VAR_MUTEX );
var_Get( p_this->p_libvlc, "tls_mutex", &lockval );
vlc_mutex_lock( lockval.p_address );
p_tls = vlc_object_find( p_this, VLC_OBJECT_TLS, FIND_ANYWHERE );
if( p_tls == NULL )
{
p_tls = vlc_object_create( p_this, VLC_OBJECT_TLS );
if( p_tls == NULL )
{
vlc_mutex_unlock( lockval.p_address );
return NULL;
}
p_tls->p_module = module_Need( p_tls, "tls", 0, 0 );
if( p_tls->p_module == NULL )
{
msg_Err( p_tls, "TLS/SSL provider not found" );
vlc_mutex_unlock( lockval.p_address );
vlc_object_destroy( p_tls );
return NULL;
}
vlc_object_attach( p_tls, p_this->p_libvlc );
vlc_object_yield( p_tls );
msg_Dbg( p_tls, "TLS/SSL provider initialized" );
}
vlc_mutex_unlock( lockval.p_address );
return p_tls;
}
static void
tls_Deinit( tls_t *p_tls )
{
int i;
vlc_value_t lockval;
var_Get( p_tls->p_libvlc, "tls_mutex", &lockval );
vlc_mutex_lock( lockval.p_address );
vlc_object_release( p_tls );
i = p_tls->i_refcount;
if( i == 0 )
vlc_object_detach( p_tls );
vlc_mutex_unlock( lockval.p_address );
if( i == 0 )
{
module_Unneed( p_tls, p_tls->p_module );
msg_Dbg( p_tls, "TLS/SSL provider deinitialized" );
vlc_object_destroy( p_tls );
}
}
/** /**
* Allocates a whole server's TLS credentials. * Allocates a whole server's TLS credentials.
* *
* @param psz_cert required (Unicode) path to an x509 certificate. * @param cert_path required (Unicode) path to an x509 certificate,
* @param psz_key required (Unicode) path to the PKCS private key for * if NULL, anonymous key exchange will be used.
* the certificate. * @param key_path (UTF-8) path to the PKCS private key for the certificate,
* if NULL; cert_path will be used.
* *
* @return NULL on error. * @return NULL on error.
*/ */
tls_server_t * tls_server_t *
tls_ServerCreate( vlc_object_t *p_this, const char *psz_cert, tls_ServerCreate (vlc_object_t *obj, const char *cert_path,
const char *psz_key ) const char *key_path)
{ {
tls_t *p_tls; tls_server_t *srv;
tls_server_t *p_server;
p_tls = tls_Init( p_this ); srv = (tls_server_t *)vlc_custom_create (obj, sizeof (*srv),
if( p_tls == NULL ) VLC_OBJECT_GENERIC,
"tls server");
if (srv == NULL)
return NULL; return NULL;
if( psz_key == NULL ) var_Create (srv, "tls-x509-cert", VLC_VAR_STRING);
psz_key = psz_cert; var_Create (srv, "tls-x509-key", VLC_VAR_STRING);
p_server = p_tls->pf_server_create( p_tls, psz_cert, psz_key ); if (cert_path != NULL)
if( p_server != NULL )
{ {
msg_Dbg( p_tls, "TLS/SSL server initialized" ); var_SetString (srv, "tls-x509-cert", cert_path);
return p_server;
if (key_path == NULL)
key_path = cert_path;
var_SetString (srv, "tls-x509-key", key_path);
} }
else
msg_Err( p_tls, "TLS/SSL server error" );
tls_Deinit( p_tls ); srv->p_module = module_Need (srv, "tls server", 0, 0);
return NULL; if (srv->p_module == NULL)
{
msg_Err (srv, "TLS server plugin not available");
vlc_object_destroy (srv);
return NULL;
}
vlc_object_attach (srv, obj);
msg_Dbg (srv, "TLS server plugin initialized");
return srv;
} }
/** /**
* Releases data allocated with tls_ServerCreate. * Releases data allocated with tls_ServerCreate.
* @param srv TLS server object to be destroyed, or NULL
*/ */
void void tls_ServerDelete (tls_server_t *srv)
tls_ServerDelete( tls_server_t *p_server )
{ {
tls_t *p_tls = (tls_t *)p_server->p_parent; if (srv == NULL)
return;
p_server->pf_delete( p_server ); module_Unneed (srv, srv->p_module);
vlc_object_detach (srv);
tls_Deinit( p_tls ); vlc_object_destroy (srv);
} }
...@@ -156,36 +105,38 @@ tls_ServerDelete( tls_server_t *p_server ) ...@@ -156,36 +105,38 @@ tls_ServerDelete( tls_server_t *p_server )
* @return NULL on error. * @return NULL on error.
**/ **/
tls_session_t * tls_session_t *
tls_ClientCreate( vlc_object_t *p_this, int fd, const char *psz_hostname ) tls_ClientCreate (vlc_object_t *obj, int fd, const char *psz_hostname)
{ {
tls_t *p_tls; tls_session_t *cl;
tls_session_t *p_session;
cl = (tls_session_t *)vlc_custom_create (obj, sizeof (*cl),
VLC_OBJECT_GENERIC,
"tls client");
if (cl == NULL)
return NULL;
p_tls = tls_Init( p_this ); cl->p_module = module_Need (cl, "tls client", 0, 0);
if( p_tls == NULL ) if (cl->p_module == NULL)
{
msg_Err (cl, "TLS client plugin not available");
vlc_object_destroy (cl);
return NULL; return NULL;
}
p_session = p_tls->pf_client_create( p_tls );
if( p_session != NULL ) int val = tls_ClientSessionHandshake (cl, fd, psz_hostname);
while (val > 0)
val = tls_SessionContinueHandshake (cl);
if (val == 0)
{ {
int i_val; msg_Dbg (cl, "TLS client session initialized");
vlc_object_attach (cl, obj);
for( i_val = tls_ClientSessionHandshake( p_session, fd, return cl;
psz_hostname );
i_val > 0;
i_val = tls_SessionContinueHandshake( p_session ) );
if( i_val == 0 )
{
msg_Dbg( p_this, "TLS/SSL client initialized" );
return p_session;
}
msg_Err( p_this, "TLS/SSL session handshake error" );
} }
else msg_Err (cl, "TLS client session handshake error");
msg_Err( p_this, "TLS/SSL client error" );
tls_Deinit( p_tls ); module_Unneed (cl, cl->p_module);
vlc_object_destroy (cl);
return NULL; return NULL;
} }
...@@ -194,12 +145,12 @@ tls_ClientCreate( vlc_object_t *p_this, int fd, const char *psz_hostname ) ...@@ -194,12 +145,12 @@ tls_ClientCreate( vlc_object_t *p_this, int fd, const char *psz_hostname )
* Releases data allocated with tls_ClientCreate. * Releases data allocated with tls_ClientCreate.
* It is your job to close the underlying socket. * It is your job to close the underlying socket.
*/ */
void void tls_ClientDelete (tls_session_t *cl)
tls_ClientDelete( tls_session_t *p_session )
{ {
tls_t *p_tls = (tls_t *)p_session->p_parent; if (cl == NULL)
return;
p_session->pf_close( p_session );
tls_Deinit( p_tls ); module_Unneed (cl, cl->p_module);
vlc_object_detach (cl);
vlc_object_destroy (cl);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment