]> git.sesse.net Git - vlc/commitdiff
tls: unify server and client credential & session concepts
authorRémi Denis-Courmont <remi@remlab.net>
Sat, 29 Sep 2012 13:10:02 +0000 (16:10 +0300)
committerRémi Denis-Courmont <remi@remlab.net>
Sat, 29 Sep 2012 18:26:43 +0000 (21:26 +0300)
The TLS plugin now supports reusing the same set of credentials for
multiple sessions also on the client side.

include/vlc_tls.h
modules/misc/gnutls.c
src/network/tls.c

index 8fe488fa7a3fb2f488c5873644e133084194f9cb..59ef7c21802632ee9530361b618c37f92f8a3162 100644 (file)
@@ -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
index c26b2d5c796f507684ee30174d8cb1869a169487..21f704f3c0a42c50c449979cfa060b27600abdac 100644 (file)
@@ -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));
+}
index 3ea6f90c047980c14d1d2904b7927ec3e920700f..07402639925bdf725c0f4f72fd3e3791e520c41f 100644 (file)
@@ -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);
 }