Commit 3b14b639 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

tls: unify server and client credential & session concepts

The TLS plugin now supports reusing the same set of credentials for
multiple sessions also on the client side.
parent b027bfd8
...@@ -39,9 +39,6 @@ struct vlc_tls ...@@ -39,9 +39,6 @@ struct vlc_tls
{ {
VLC_COMMON_MEMBERS VLC_COMMON_MEMBERS
union {
module_t *module; /**< Plugin handle (client) */
} u;
vlc_tls_sys_t *sys; vlc_tls_sys_t *sys;
struct virtual_socket_t sock; struct virtual_socket_t sock;
...@@ -69,7 +66,7 @@ struct vlc_tls_creds ...@@ -69,7 +66,7 @@ struct vlc_tls_creds
int (*add_CA) (vlc_tls_creds_t *, const char *path); int (*add_CA) (vlc_tls_creds_t *, const char *path);
int (*add_CRL) (vlc_tls_creds_t *, const char *path); int (*add_CRL) (vlc_tls_creds_t *, const char *path);
int (*open) (vlc_tls_creds_t *, vlc_tls_t *, int fd); int (*open) (vlc_tls_creds_t *, vlc_tls_t *, int fd, const char *host);
void (*close) (vlc_tls_creds_t *, vlc_tls_t *); void (*close) (vlc_tls_creds_t *, vlc_tls_t *);
}; };
...@@ -82,6 +79,7 @@ int vlc_tls_ServerAddCRL (vlc_tls_creds_t *srv, const char *path); ...@@ -82,6 +79,7 @@ int vlc_tls_ServerAddCRL (vlc_tls_creds_t *srv, const char *path);
vlc_tls_t *vlc_tls_ServerSessionCreate (vlc_tls_creds_t *, int fd); vlc_tls_t *vlc_tls_ServerSessionCreate (vlc_tls_creds_t *, int fd);
int vlc_tls_ServerSessionHandshake (vlc_tls_t *); int vlc_tls_ServerSessionHandshake (vlc_tls_t *);
void vlc_tls_ServerSessionDelete (vlc_tls_t *); void vlc_tls_SessionDelete (vlc_tls_t *);
#define vlc_tls_ServerSessionDelete vlc_tls_SessionDelete
#endif #endif
...@@ -46,8 +46,8 @@ ...@@ -46,8 +46,8 @@
/***************************************************************************** /*****************************************************************************
* Module descriptor * Module descriptor
*****************************************************************************/ *****************************************************************************/
static int OpenClient (vlc_tls_t *, int, const char *); static int OpenClient (vlc_tls_creds_t *);
static void CloseClient (vlc_tls_t *); static void CloseClient (vlc_tls_creds_t *);
static int OpenServer (vlc_tls_creds_t *, const char *, const char *); static int OpenServer (vlc_tls_creds_t *, const char *, const char *);
static void CloseServer (vlc_tls_creds_t *); static void CloseServer (vlc_tls_creds_t *);
...@@ -176,8 +176,7 @@ static int gnutls_Error (vlc_object_t *obj, int val) ...@@ -176,8 +176,7 @@ static int gnutls_Error (vlc_object_t *obj, int val)
struct vlc_tls_sys struct vlc_tls_sys
{ {
gnutls_session_t session; gnutls_session_t session;
gnutls_certificate_credentials_t x509_cred; char *hostname; /* XXX: client only */
char *hostname;
bool handshaked; bool handshaked;
}; };
...@@ -366,124 +365,15 @@ gnutls_SessionPrioritize (vlc_object_t *obj, gnutls_session_t session) ...@@ -366,124 +365,15 @@ gnutls_SessionPrioritize (vlc_object_t *obj, gnutls_session_t session)
return val; return val;
} }
/**
* Initializes a client-side TLS session.
*/
static int OpenClient (vlc_tls_t *session, int fd, const char *hostname)
{
if (gnutls_Init (VLC_OBJECT(session)))
return VLC_EGENERIC;
vlc_tls_sys_t *sys = malloc (sizeof (*sys));
if (unlikely(sys == NULL))
{
gnutls_Deinit (VLC_OBJECT(session));
return VLC_ENOMEM;
}
session->sys = sys;
session->sock.p_sys = session;
session->sock.pf_send = gnutls_Send;
session->sock.pf_recv = gnutls_Recv;
sys->handshaked = false;
int val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
if (val != 0)
{
msg_Err (session, "cannot allocate credentials: %s",
gnutls_strerror (val));
goto error;
}
val = gnutls_certificate_set_x509_system_trust (sys->x509_cred);
if (val < 0)
msg_Err (session, "cannot load trusted Certificate Authorities: %s",
gnutls_strerror (val));
else
msg_Dbg (session, "loaded %d trusted CAs", val);
gnutls_certificate_set_verify_flags (sys->x509_cred,
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
session->handshake = gnutls_HandshakeAndValidate;
/*session->_handshake = gnutls_ContinueHandshake;*/
val = gnutls_init (&sys->session, GNUTLS_CLIENT);
if (val != 0)
{
msg_Err (session, "cannot initialize TLS session: %s",
gnutls_strerror (val));
gnutls_certificate_free_credentials (sys->x509_cred);
goto error;
}
if (gnutls_SessionPrioritize (VLC_OBJECT(session), sys->session))
goto s_error;
/* minimum DH prime bits */
gnutls_dh_set_prime_bits (sys->session, 1024);
val = gnutls_credentials_set (sys->session, GNUTLS_CRD_CERTIFICATE,
sys->x509_cred);
if (val < 0)
{
msg_Err (session, "cannot set TLS session credentials: %s",
gnutls_strerror (val));
goto s_error;
}
/* server name */
if (likely(hostname != NULL))
{
/* fill Server Name Indication */
gnutls_server_name_set (sys->session, GNUTLS_NAME_DNS,
hostname, strlen (hostname));
/* keep hostname to match CNAME after handshake */
sys->hostname = strdup (hostname);
if (unlikely(sys->hostname == NULL))
goto s_error;
}
else
sys->hostname = NULL;
gnutls_transport_set_ptr (sys->session,
(gnutls_transport_ptr_t)(intptr_t)fd);
return VLC_SUCCESS;
s_error:
gnutls_deinit (sys->session);
gnutls_certificate_free_credentials (sys->x509_cred);
error:
gnutls_Deinit (VLC_OBJECT(session));
free (sys);
return VLC_EGENERIC;
}
static void CloseClient (vlc_tls_t *session)
{
vlc_tls_sys_t *sys = session->sys;
if (sys->handshaked)
gnutls_bye (sys->session, GNUTLS_SHUT_WR);
gnutls_deinit (sys->session);
/* credentials must be free'd *after* gnutls_deinit() */
gnutls_certificate_free_credentials (sys->x509_cred);
gnutls_Deinit (VLC_OBJECT(session));
free (sys->hostname);
free (sys);
}
/** /**
* Server-side TLS * TLS credentials private data
*/ */
struct vlc_tls_creds_sys struct vlc_tls_creds_sys
{ {
gnutls_certificate_credentials_t x509_cred; gnutls_certificate_credentials_t x509_cred;
gnutls_dh_params_t dh_params; gnutls_dh_params_t dh_params; /* XXX: used for server only */
int (*handshake) (vlc_tls_t *); int (*handshake) (vlc_tls_t *); /* XXX: useful for server only */
}; };
...@@ -499,6 +389,7 @@ static void gnutls_SessionClose (vlc_tls_creds_t *crd, vlc_tls_t *session) ...@@ -499,6 +389,7 @@ static void gnutls_SessionClose (vlc_tls_creds_t *crd, vlc_tls_t *session)
gnutls_bye (sys->session, GNUTLS_SHUT_WR); gnutls_bye (sys->session, GNUTLS_SHUT_WR);
gnutls_deinit (sys->session); gnutls_deinit (sys->session);
free (sys->hostname);
free (sys); free (sys);
(void) crd; (void) crd;
} }
...@@ -508,7 +399,7 @@ static void gnutls_SessionClose (vlc_tls_creds_t *crd, vlc_tls_t *session) ...@@ -508,7 +399,7 @@ static void gnutls_SessionClose (vlc_tls_creds_t *crd, vlc_tls_t *session)
* Initializes a server-side TLS session. * Initializes a server-side TLS session.
*/ */
static int gnutls_SessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session, static int gnutls_SessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
int fd) int type, int fd)
{ {
vlc_tls_sys_t *sys = malloc (sizeof (*session->sys)); vlc_tls_sys_t *sys = malloc (sizeof (*session->sys));
if (unlikely(sys == NULL)) if (unlikely(sys == NULL))
...@@ -522,7 +413,7 @@ static int gnutls_SessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session, ...@@ -522,7 +413,7 @@ static int gnutls_SessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
sys->handshaked = false; sys->handshaked = false;
sys->hostname = NULL; sys->hostname = NULL;
int val = gnutls_init (&sys->session, GNUTLS_SERVER); int val = gnutls_init (&sys->session, type);
if (val != 0) if (val != 0)
{ {
msg_Err (session, "cannot initialize TLS session: %s", msg_Err (session, "cannot initialize TLS session: %s",
...@@ -543,10 +434,6 @@ static int gnutls_SessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session, ...@@ -543,10 +434,6 @@ static int gnutls_SessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
goto error; goto error;
} }
if (session->handshake == gnutls_HandshakeAndValidate)
gnutls_certificate_server_set_request (sys->session,
GNUTLS_CERT_REQUIRE);
gnutls_transport_set_ptr (sys->session, gnutls_transport_set_ptr (sys->session,
(gnutls_transport_ptr_t)(intptr_t)fd); (gnutls_transport_ptr_t)(intptr_t)fd);
return VLC_SUCCESS; return VLC_SUCCESS;
...@@ -556,6 +443,50 @@ error: ...@@ -556,6 +443,50 @@ error:
return VLC_EGENERIC; return VLC_EGENERIC;
} }
static int gnutls_ServerSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
int fd, const char *hostname)
{
int val = gnutls_SessionOpen (crd, session, GNUTLS_SERVER, fd);
if (val != VLC_SUCCESS)
return val;
if (session->handshake == gnutls_HandshakeAndValidate)
gnutls_certificate_server_set_request (session->sys->session,
GNUTLS_CERT_REQUIRE);
assert (hostname == NULL);
return VLC_SUCCESS;
}
static int gnutls_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
int fd, const char *hostname)
{
int val = gnutls_SessionOpen (crd, session, GNUTLS_CLIENT, fd);
if (val != VLC_SUCCESS)
return val;
vlc_tls_sys_t *sys = session->sys;
/* minimum DH prime bits */
gnutls_dh_set_prime_bits (sys->session, 1024);
/* server name */
if (likely(hostname != NULL))
{
/* fill Server Name Indication */
gnutls_server_name_set (sys->session, GNUTLS_NAME_DNS,
hostname, strlen (hostname));
/* keep hostname to match CNAME after handshake */
sys->hostname = strdup (hostname);
if (unlikely(sys->hostname == NULL))
goto error;
}
return VLC_SUCCESS;
error:
gnutls_SessionClose (crd, session);
return VLC_EGENERIC;
}
/** /**
* Adds one or more Certificate Authorities to the trusted set. * Adds one or more Certificate Authorities to the trusted set.
...@@ -647,7 +578,7 @@ static int OpenServer (vlc_tls_creds_t *crd, const char *cert, const char *key) ...@@ -647,7 +578,7 @@ static int OpenServer (vlc_tls_creds_t *crd, const char *cert, const char *key)
crd->sys = sys; crd->sys = sys;
crd->add_CA = gnutls_AddCA; crd->add_CA = gnutls_AddCA;
crd->add_CRL = gnutls_AddCRL; crd->add_CRL = gnutls_AddCRL;
crd->open = gnutls_SessionOpen; crd->open = gnutls_ServerSessionOpen;
crd->close = gnutls_SessionClose; crd->close = gnutls_SessionClose;
/* No certificate validation by default */ /* No certificate validation by default */
sys->handshake = gnutls_ContinueHandshake; sys->handshake = gnutls_ContinueHandshake;
...@@ -740,3 +671,57 @@ static void CloseServer (vlc_tls_creds_t *crd) ...@@ -740,3 +671,57 @@ static void CloseServer (vlc_tls_creds_t *crd)
gnutls_Deinit (VLC_OBJECT(crd)); gnutls_Deinit (VLC_OBJECT(crd));
} }
/**
* Initializes a client-side TLS credentials.
*/
static int OpenClient (vlc_tls_creds_t *crd)
{
if (gnutls_Init (VLC_OBJECT(crd)))
return VLC_EGENERIC;
vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
if (unlikely(sys == NULL))
goto error;
crd->sys = sys;
//crd->add_CA = gnutls_AddCA;
//crd->add_CRL = gnutls_AddCRL;
crd->open = gnutls_ClientSessionOpen;
crd->close = gnutls_SessionClose;
sys->handshake = gnutls_HandshakeAndValidate;
int val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
if (val != 0)
{
msg_Err (crd, "cannot allocate credentials: %s",
gnutls_strerror (val));
goto error;
}
val = gnutls_certificate_set_x509_system_trust (sys->x509_cred);
if (val < 0)
msg_Err (crd, "cannot load trusted Certificate Authorities: %s",
gnutls_strerror (val));
else
msg_Dbg (crd, "loaded %d trusted CAs", val);
gnutls_certificate_set_verify_flags (sys->x509_cred,
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
return VLC_SUCCESS;
error:
free (sys);
gnutls_Deinit (VLC_OBJECT(crd));
return VLC_EGENERIC;
}
static void CloseClient (vlc_tls_creds_t *crd)
{
vlc_tls_creds_sys_t *sys = crd->sys;
gnutls_certificate_free_credentials (sys->x509_cred);
free (sys);
gnutls_Deinit (VLC_OBJECT(crd));
}
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
#include <vlc_tls.h> #include <vlc_tls.h>
#include <vlc_modules.h> #include <vlc_modules.h>
/*** TLS credentials ***/
static int tls_server_load(void *func, va_list ap) static int tls_server_load(void *func, va_list ap)
{ {
int (*activate) (vlc_tls_creds_t *, const char *, const char *) = func; int (*activate) (vlc_tls_creds_t *, const char *, const char *) = func;
...@@ -46,6 +48,14 @@ static int tls_server_load(void *func, va_list ap) ...@@ -46,6 +48,14 @@ static int tls_server_load(void *func, va_list ap)
return activate (crd, cert, key); return activate (crd, cert, key);
} }
static int tls_client_load(void *func, va_list ap)
{
int (*activate) (vlc_tls_creds_t *) = func;
vlc_tls_creds_t *crd = va_arg (ap, vlc_tls_creds_t *);
return activate (crd);
}
static void tls_unload(void *func, va_list ap) static void tls_unload(void *func, va_list ap)
{ {
void (*deactivate) (vlc_tls_creds_t *) = func; void (*deactivate) (vlc_tls_creds_t *) = func;
...@@ -68,7 +78,8 @@ vlc_tls_creds_t * ...@@ -68,7 +78,8 @@ vlc_tls_creds_t *
vlc_tls_ServerCreate (vlc_object_t *obj, const char *cert_path, vlc_tls_ServerCreate (vlc_object_t *obj, const char *cert_path,
const char *key_path) const char *key_path)
{ {
vlc_tls_creds_t *srv = vlc_custom_create (obj, sizeof (*srv), "tls creds"); vlc_tls_creds_t *srv = vlc_custom_create (obj, sizeof (*srv),
"tls server");
if (unlikely(srv == NULL)) if (unlikely(srv == NULL))
return NULL; return NULL;
...@@ -123,19 +134,21 @@ int vlc_tls_ServerAddCRL (vlc_tls_creds_t *srv, const char *path) ...@@ -123,19 +134,21 @@ int vlc_tls_ServerAddCRL (vlc_tls_creds_t *srv, const char *path)
} }
vlc_tls_t *vlc_tls_ServerSessionCreate (vlc_tls_creds_t *crd, int fd) /*** TLS session ***/
static vlc_tls_t *vlc_tls_SessionCreate (vlc_tls_creds_t *crd, int fd,
const char *hostname)
{ {
vlc_tls_t *session = vlc_custom_create (crd, sizeof (*session), vlc_tls_t *session = vlc_custom_create (crd, sizeof (*session),
"tls server"); "tls session");
int val = crd->open (crd, session, fd); int val = crd->open (crd, session, fd, hostname);
if (val == VLC_SUCCESS) if (val == VLC_SUCCESS)
return session; return session;
vlc_object_release (session); vlc_object_release (session);
return NULL; return NULL;
} }
void vlc_tls_SessionDelete (vlc_tls_t *session)
void vlc_tls_ServerSessionDelete (vlc_tls_t *session)
{ {
vlc_tls_creds_t *crd = (vlc_tls_creds_t *)(session->p_parent); vlc_tls_creds_t *crd = (vlc_tls_creds_t *)(session->p_parent);
...@@ -143,6 +156,10 @@ void vlc_tls_ServerSessionDelete (vlc_tls_t *session) ...@@ -143,6 +156,10 @@ void vlc_tls_ServerSessionDelete (vlc_tls_t *session)
vlc_object_release (session); vlc_object_release (session);
} }
vlc_tls_t *vlc_tls_ServerSessionCreate (vlc_tls_creds_t *crd, int fd)
{
return vlc_tls_SessionCreate (crd, fd, NULL);
}
int vlc_tls_ServerSessionHandshake (vlc_tls_t *ses) int vlc_tls_ServerSessionHandshake (vlc_tls_t *ses)
{ {
...@@ -152,28 +169,6 @@ int vlc_tls_ServerSessionHandshake (vlc_tls_t *ses) ...@@ -152,28 +169,6 @@ int vlc_tls_ServerSessionHandshake (vlc_tls_t *ses)
return val; return val;
} }
/*** TLS client session ***/
/* TODO: cache certificates for the whole VLC instance lifetime */
static int tls_client_start(void *func, va_list ap)
{
int (*activate) (vlc_tls_t *, int fd, const char *hostname) = func;
vlc_tls_t *session = va_arg (ap, vlc_tls_t *);
int fd = va_arg (ap, int);
const char *hostname = va_arg (ap, const char *);
return activate (session, fd, hostname);
}
static void tls_client_stop(void *func, va_list ap)
{
void (*deactivate) (vlc_tls_t *) = func;
vlc_tls_t *session = va_arg (ap, vlc_tls_t *);
deactivate (session);
}
/** /**
* Allocates a client's TLS credentials and shakes hands through the network. * Allocates a client's TLS credentials and shakes hands through the network.
* This is a blocking network operation. * This is a blocking network operation.
...@@ -187,34 +182,43 @@ static void tls_client_stop(void *func, va_list ap) ...@@ -187,34 +182,43 @@ static void tls_client_stop(void *func, va_list ap)
vlc_tls_t * vlc_tls_t *
vlc_tls_ClientCreate (vlc_object_t *obj, int fd, const char *hostname) vlc_tls_ClientCreate (vlc_object_t *obj, int fd, const char *hostname)
{ {
vlc_tls_t *cl = vlc_custom_create (obj, sizeof (*cl), "tls client"); vlc_tls_creds_t *crd = vlc_custom_create (obj, sizeof (*crd),
if (unlikely(cl == NULL)) "tls client");
if (unlikely(crd == NULL))
return NULL; return NULL;
cl->u.module = vlc_module_load (cl, "tls client", NULL, false, crd->module = vlc_module_load (crd, "tls client", NULL, false,
tls_client_start, cl, fd, hostname); tls_client_load, crd);
if (cl->u.module == NULL) if (crd->module == NULL)
{ {
msg_Err (cl, "TLS client plugin not available"); msg_Err (crd, "TLS client plugin not available");
vlc_object_release (cl); vlc_object_release (crd);
return NULL; return NULL;
} }
/* TODO: separate credentials and sessions, so we do not reload the
* credentials every time the HTTP access seeks... */
vlc_tls_t *session = vlc_tls_SessionCreate (crd, fd, hostname);
if (session == NULL)
goto error;
/* TODO: do this directly in the TLS plugin */ /* TODO: do this directly in the TLS plugin */
int val; int val;
do do
val = cl->handshake (cl); val = session->handshake (session);
while (val > 0); while (val > 0);
if (val != 0) if (val != 0)
{ {
msg_Err (cl, "TLS client session handshake error"); msg_Err (session, "TLS client session handshake error");
vlc_module_unload (cl->u.module, tls_client_stop, cl); vlc_tls_SessionDelete (session);
vlc_object_release (cl); goto error;
return NULL;
} }
msg_Dbg (cl, "TLS client session initialized"); msg_Dbg (session, "TLS client session initialized");
return cl; return session;
error:
vlc_tls_Delete (crd);
return NULL;
} }
...@@ -222,11 +226,13 @@ vlc_tls_ClientCreate (vlc_object_t *obj, int fd, const char *hostname) ...@@ -222,11 +226,13 @@ vlc_tls_ClientCreate (vlc_object_t *obj, int fd, const char *hostname)
* Releases data allocated with vlc_tls_ClientCreate(). * Releases data allocated with vlc_tls_ClientCreate().
* It is your job to close the underlying socket. * It is your job to close the underlying socket.
*/ */
void vlc_tls_ClientDelete (vlc_tls_t *cl) void vlc_tls_ClientDelete (vlc_tls_t *session)
{ {
if (cl == NULL) if (session == NULL)
return; return;
vlc_module_unload (cl->u.module, tls_client_stop, cl); vlc_tls_creds_t *cl = (vlc_tls_creds_t *)(session->p_parent);
vlc_object_release (cl);
vlc_tls_SessionDelete (session);
vlc_tls_Delete (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