From 3b14b63995cae2a8f92b66acf14679e4a7f71cc5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?R=C3=A9mi=20Denis-Courmont?= Date: Sat, 29 Sep 2012 16:10:02 +0300 Subject: [PATCH] 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. --- include/vlc_tls.h | 8 +- modules/misc/gnutls.c | 231 ++++++++++++++++++++---------------------- src/network/tls.c | 98 +++++++++--------- 3 files changed, 163 insertions(+), 174 deletions(-) diff --git a/include/vlc_tls.h b/include/vlc_tls.h index 8fe488fa7a..59ef7c2180 100644 --- a/include/vlc_tls.h +++ b/include/vlc_tls.h @@ -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 diff --git a/modules/misc/gnutls.c b/modules/misc/gnutls.c index c26b2d5c79..21f704f3c0 100644 --- a/modules/misc/gnutls.c +++ b/modules/misc/gnutls.c @@ -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)); +} diff --git a/src/network/tls.c b/src/network/tls.c index 3ea6f90c04..0740263992 100644 --- a/src/network/tls.c +++ b/src/network/tls.c @@ -36,6 +36,8 @@ #include #include +/*** 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); } -- 2.39.5