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
{
VLC_COMMON_MEMBERS
union {
module_t *module; /**< Plugin handle (client) */
} u;
vlc_tls_sys_t *sys;
struct virtual_socket_t sock;
......@@ -69,7 +66,7 @@ struct vlc_tls_creds
int (*add_CA) (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 *);
};
......@@ -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);
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
......@@ -46,8 +46,8 @@
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int OpenClient (vlc_tls_t *, int, const char *);
static void CloseClient (vlc_tls_t *);
static int OpenClient (vlc_tls_creds_t *);
static void CloseClient (vlc_tls_creds_t *);
static int OpenServer (vlc_tls_creds_t *, const char *, const char *);
static void CloseServer (vlc_tls_creds_t *);
......@@ -176,8 +176,7 @@ static int gnutls_Error (vlc_object_t *obj, int val)
struct vlc_tls_sys
{
gnutls_session_t session;
gnutls_certificate_credentials_t x509_cred;
char *hostname;
char *hostname; /* XXX: client only */
bool handshaked;
};
......@@ -366,124 +365,15 @@ gnutls_SessionPrioritize (vlc_object_t *obj, gnutls_session_t session)
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
{
gnutls_certificate_credentials_t x509_cred;
gnutls_dh_params_t dh_params;
int (*handshake) (vlc_tls_t *);
gnutls_dh_params_t dh_params; /* XXX: used for server only */
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)
gnutls_bye (sys->session, GNUTLS_SHUT_WR);
gnutls_deinit (sys->session);
free (sys->hostname);
free (sys);
(void) crd;
}
......@@ -508,7 +399,7 @@ static void gnutls_SessionClose (vlc_tls_creds_t *crd, vlc_tls_t *session)
* Initializes a server-side TLS 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));
if (unlikely(sys == NULL))
......@@ -522,7 +413,7 @@ static int gnutls_SessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
sys->handshaked = false;
sys->hostname = NULL;
int val = gnutls_init (&sys->session, GNUTLS_SERVER);
int val = gnutls_init (&sys->session, type);
if (val != 0)
{
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,
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_ptr_t)(intptr_t)fd);
return VLC_SUCCESS;
......@@ -556,6 +443,50 @@ error:
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.
......@@ -647,7 +578,7 @@ static int OpenServer (vlc_tls_creds_t *crd, const char *cert, const char *key)
crd->sys = sys;
crd->add_CA = gnutls_AddCA;
crd->add_CRL = gnutls_AddCRL;
crd->open = gnutls_SessionOpen;
crd->open = gnutls_ServerSessionOpen;
crd->close = gnutls_SessionClose;
/* No certificate validation by default */
sys->handshake = gnutls_ContinueHandshake;
......@@ -740,3 +671,57 @@ static void CloseServer (vlc_tls_creds_t *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 @@
#include <vlc_tls.h>
#include <vlc_modules.h>
/*** TLS credentials ***/
static int tls_server_load(void *func, va_list ap)
{
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)
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)
{
void (*deactivate) (vlc_tls_creds_t *) = func;
......@@ -68,7 +78,8 @@ vlc_tls_creds_t *
vlc_tls_ServerCreate (vlc_object_t *obj, const char *cert_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))
return NULL;
......@@ -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),
"tls server");
int val = crd->open (crd, session, fd);
"tls session");
int val = crd->open (crd, session, fd, hostname);
if (val == VLC_SUCCESS)
return session;
vlc_object_release (session);
return NULL;
}
void vlc_tls_ServerSessionDelete (vlc_tls_t *session)
void vlc_tls_SessionDelete (vlc_tls_t *session)
{
vlc_tls_creds_t *crd = (vlc_tls_creds_t *)(session->p_parent);
......@@ -143,6 +156,10 @@ void vlc_tls_ServerSessionDelete (vlc_tls_t *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)
{
......@@ -152,28 +169,6 @@ int vlc_tls_ServerSessionHandshake (vlc_tls_t *ses)
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.
* This is a blocking network operation.
......@@ -187,34 +182,43 @@ static void tls_client_stop(void *func, va_list ap)
vlc_tls_t *
vlc_tls_ClientCreate (vlc_object_t *obj, int fd, const char *hostname)
{
vlc_tls_t *cl = vlc_custom_create (obj, sizeof (*cl), "tls client");
if (unlikely(cl == NULL))
vlc_tls_creds_t *crd = vlc_custom_create (obj, sizeof (*crd),
"tls client");
if (unlikely(crd == NULL))
return NULL;
cl->u.module = vlc_module_load (cl, "tls client", NULL, false,
tls_client_start, cl, fd, hostname);
if (cl->u.module == NULL)
crd->module = vlc_module_load (crd, "tls client", NULL, false,
tls_client_load, crd);
if (crd->module == NULL)
{
msg_Err (cl, "TLS client plugin not available");
vlc_object_release (cl);
msg_Err (crd, "TLS client plugin not available");
vlc_object_release (crd);
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 */
int val;
do
val = cl->handshake (cl);
val = session->handshake (session);
while (val > 0);
if (val != 0)
{
msg_Err (cl, "TLS client session handshake error");
vlc_module_unload (cl->u.module, tls_client_stop, cl);
vlc_object_release (cl);
return NULL;
msg_Err (session, "TLS client session handshake error");
vlc_tls_SessionDelete (session);
goto error;
}
msg_Dbg (cl, "TLS client session initialized");
return cl;
msg_Dbg (session, "TLS client session initialized");
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)
* Releases data allocated with vlc_tls_ClientCreate().
* 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;
vlc_module_unload (cl->u.module, tls_client_stop, cl);
vlc_object_release (cl);
vlc_tls_creds_t *cl = (vlc_tls_creds_t *)(session->p_parent);
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