/*****************************************************************************
* 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 *);
struct vlc_tls_sys
{
gnutls_session_t session;
- gnutls_certificate_credentials_t x509_cred;
- char *hostname;
+ char *hostname; /* XXX: client only */
bool handshaked;
};
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 */
};
gnutls_bye (sys->session, GNUTLS_SHUT_WR);
gnutls_deinit (sys->session);
+ free (sys->hostname);
free (sys);
(void) crd;
}
* 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))
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",
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;
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.
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;
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));
+}
#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;
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;
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;
}
-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);
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)
{
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.
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;
}
* 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);
}