/*****************************************************************************
* gnutls.c
*****************************************************************************
- * Copyright (C) 2004-2012 Rémi Denis-Courmont
+ * Copyright (C) 2004-2014 Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
-/*****************************************************************************
- * Preamble
- *****************************************************************************/
-
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
#include <errno.h>
#include <assert.h>
#include <vlc_plugin.h>
#include <vlc_tls.h>
#include <vlc_block.h>
+#include <vlc_dialog.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
-#if (GNUTLS_VERSION_NUMBER < 0x030014)
-# define gnutls_certificate_set_x509_system_trust(c) \
- (c, GNUTLS_E_UNIMPLEMENTED_FEATURE)
-#endif
-
#include "dhparams.h"
-/*****************************************************************************
- * Module descriptor
- *****************************************************************************/
-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 *);
-
-#define PRIORITIES_TEXT N_("TLS cipher priorities")
-#define PRIORITIES_LONGTEXT N_("Ciphers, key exchange methods, " \
- "hash functions and compression methods can be selected. " \
- "Refer to GNU TLS documentation for detailed syntax.")
-static const char *const priorities_values[] = {
- "PERFORMANCE",
- "NORMAL",
- "SECURE128",
- "SECURE256",
- "EXPORT",
-};
-static const char *const priorities_text[] = {
- N_("Performance (prioritize faster ciphers)"),
- N_("Normal"),
- N_("Secure 128-bits (exclude 256-bits ciphers)"),
- N_("Secure 256-bits (prioritize 256-bits ciphers)"),
- N_("Export (include insecure ciphers)"),
-};
-
-vlc_module_begin ()
- set_shortname( "GNU TLS" )
- set_description( N_("GNU TLS transport layer security") )
- set_capability( "tls client", 1 )
- set_callbacks( OpenClient, CloseClient )
- set_category( CAT_ADVANCED )
- set_subcategory( SUBCAT_ADVANCED_MISC )
-
- add_submodule ()
- set_description( N_("GNU TLS server") )
- set_capability( "tls server", 1 )
- set_category( CAT_ADVANCED )
- set_subcategory( SUBCAT_ADVANCED_MISC )
- set_callbacks( OpenServer, CloseServer )
-
- add_string ("gnutls-priorities", "NORMAL", PRIORITIES_TEXT,
- PRIORITIES_LONGTEXT, false)
- change_string_list (priorities_values, priorities_text)
-vlc_module_end ()
+#if (GNUTLS_VERSION_NUMBER >= 0x030300)
+static int gnutls_Init (vlc_object_t *obj)
+{
+ const char *version = gnutls_check_version ("3.3.0");
+ if (version == NULL)
+ {
+ msg_Err (obj, "unsupported GnuTLS version");
+ return -1;
+ }
+ msg_Dbg (obj, "using GnuTLS version %s", version);
+ return 0;
+}
+# define gnutls_Deinit() (void)0
+#else
static vlc_mutex_t gnutls_mutex = VLC_STATIC_MUTEX;
/**
* Initializes GnuTLS with proper locking.
* @return VLC_SUCCESS on success, a VLC error code otherwise.
*/
-static int gnutls_Init (vlc_object_t *p_this)
+static int gnutls_Init (vlc_object_t *obj)
{
- int ret = VLC_EGENERIC;
-
- vlc_mutex_lock (&gnutls_mutex);
- if (gnutls_global_init ())
+ const char *version = gnutls_check_version ("3.1.11");
+ if (version == NULL)
{
- msg_Err (p_this, "cannot initialize GnuTLS");
- goto error;
+ msg_Err (obj, "unsupported GnuTLS version");
+ return -1;
}
+ msg_Dbg (obj, "using GnuTLS version %s", version);
- const char *psz_version = gnutls_check_version ("2.6.6");
- if (psz_version == NULL)
+ if (gnutls_check_version ("3.3.0") == NULL)
{
- msg_Err (p_this, "unsupported GnuTLS version");
- gnutls_global_deinit ();
- goto error;
- }
+ int val;
- msg_Dbg (p_this, "GnuTLS v%s initialized", psz_version);
- ret = VLC_SUCCESS;
+ vlc_mutex_lock (&gnutls_mutex);
+ val = gnutls_global_init ();
+ vlc_mutex_unlock (&gnutls_mutex);
-error:
- vlc_mutex_unlock (&gnutls_mutex);
- return ret;
+ if (val)
+ {
+ msg_Err (obj, "cannot initialize GnuTLS");
+ return -1;
+ }
+ }
+ return 0;
}
-
/**
* Deinitializes GnuTLS.
*/
-static void gnutls_Deinit (vlc_object_t *p_this)
+static void gnutls_Deinit (void)
{
vlc_mutex_lock (&gnutls_mutex);
-
gnutls_global_deinit ();
- msg_Dbg (p_this, "GnuTLS deinitialized");
vlc_mutex_unlock (&gnutls_mutex);
}
-
+#endif
static int gnutls_Error (vlc_object_t *obj, int val)
{
switch (val)
{
case GNUTLS_E_AGAIN:
-#ifdef WIN32
+#ifdef _WIN32
WSASetLastError (WSAEWOULDBLOCK);
#else
errno = EAGAIN;
break;
case GNUTLS_E_INTERRUPTED:
-#ifdef WIN32
+#ifdef _WIN32
WSASetLastError (WSAEINTR);
#else
errno = EINTR;
if (!gnutls_error_is_fatal (val))
msg_Err (obj, "Error above should be handled");
#endif
-#ifdef WIN32
+#ifdef _WIN32
WSASetLastError (WSAECONNRESET);
#else
errno = ECONNRESET;
}
#define gnutls_Error(o, val) gnutls_Error(VLC_OBJECT(o), val)
-struct vlc_tls_sys
-{
- gnutls_session_t session;
- char *hostname; /* XXX: client only */
- bool handshaked;
-};
-
-
/**
* Sends data through a TLS session.
*/
static int gnutls_Send (void *opaque, const void *buf, size_t length)
{
- vlc_tls_t *session = opaque;
- vlc_tls_sys_t *sys = session->sys;
+ assert (opaque != NULL);
- int val = gnutls_record_send (sys->session, buf, length);
- return (val < 0) ? gnutls_Error (session, val) : val;
+ vlc_tls_t *tls = opaque;
+ gnutls_session_t session = tls->sys;
+
+ int val = gnutls_record_send (session, buf, length);
+ return (val < 0) ? gnutls_Error (tls, val) : val;
}
*/
static int gnutls_Recv (void *opaque, void *buf, size_t length)
{
- vlc_tls_t *session = opaque;
- vlc_tls_sys_t *sys = session->sys;
+ assert (opaque != NULL);
+
+ vlc_tls_t *tls = opaque;
+ gnutls_session_t session = tls->sys;
- int val = gnutls_record_recv (sys->session, buf, length);
- return (val < 0) ? gnutls_Error (session, val) : val;
+ int val = gnutls_record_recv (session, buf, length);
+ return (val < 0) ? gnutls_Error (tls, val) : val;
}
+static int gnutls_SessionOpen (vlc_tls_t *tls, int type,
+ gnutls_certificate_credentials_t x509, int fd,
+ const char *const *alpn)
+{
+ gnutls_session_t session;
+ const char *errp;
+ int val;
+
+ val = gnutls_init (&session, type);
+ if (val != 0)
+ {
+ msg_Err (tls, "cannot initialize TLS session: %s",
+ gnutls_strerror (val));
+ return VLC_EGENERIC;
+ }
+
+ char *priorities = var_InheritString (tls, "gnutls-priorities");
+ if (unlikely(priorities == NULL))
+ goto error;
+
+ val = gnutls_priority_set_direct (session, priorities, &errp);
+ if (val < 0)
+ msg_Err (tls, "cannot set TLS priorities \"%s\": %s", errp,
+ gnutls_strerror (val));
+ free (priorities);
+ if (val < 0)
+ goto error;
+
+ val = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509);
+ if (val < 0)
+ {
+ msg_Err (tls, "cannot set TLS session credentials: %s",
+ gnutls_strerror (val));
+ goto error;
+ }
+
+ if (alpn != NULL)
+ {
+ gnutls_datum_t *protv = NULL;
+ unsigned protc = 0;
+
+ while (*alpn != NULL)
+ {
+ gnutls_datum_t *n = realloc(protv, sizeof (*protv) * (protc + 1));
+ if (unlikely(n == NULL))
+ {
+ free(protv);
+ goto error;
+ }
+ protv = n;
+
+ protv[protc].data = (void *)*alpn;
+ protv[protc].size = strlen(*alpn);
+ protc++;
+ alpn++;
+ }
+
+ val = gnutls_alpn_set_protocols (session, protv, protc, 0);
+ free (protv);
+ }
+
+ gnutls_transport_set_int (session, fd);
+
+ tls->sys = session;
+ tls->sock.p_sys = NULL;
+ tls->sock.pf_send = gnutls_Send;
+ tls->sock.pf_recv = gnutls_Recv;
+ return VLC_SUCCESS;
+
+error:
+ gnutls_deinit (session);
+ return VLC_EGENERIC;
+}
/**
* Starts or continues the TLS handshake.
* 1 if more would-be blocking recv is needed,
* 2 if more would-be blocking send is required.
*/
-static int gnutls_ContinueHandshake (vlc_tls_t *session)
+static int gnutls_ContinueHandshake (vlc_tls_t *tls, char **restrict alp)
{
- vlc_tls_sys_t *sys = session->sys;
+ gnutls_session_t session = tls->sys;
int val;
-#ifdef WIN32
+#ifdef _WIN32
WSASetLastError (0);
#endif
- val = gnutls_handshake (sys->session);
- if ((val == GNUTLS_E_AGAIN) || (val == GNUTLS_E_INTERRUPTED))
- return 1 + gnutls_record_get_direction (sys->session);
-
- if (val < 0)
+ do
{
-#ifdef WIN32
- msg_Dbg (session, "Winsock error %d", WSAGetLastError ());
-#endif
- msg_Err (session, "TLS handshake error: %s", gnutls_strerror (val));
- return -1;
+ val = gnutls_handshake (session);
+ msg_Dbg (tls, "TLS handshake: %s", gnutls_strerror (val));
+
+ switch (val)
+ {
+ case GNUTLS_E_SUCCESS:
+ goto done;
+ case GNUTLS_E_AGAIN:
+ case GNUTLS_E_INTERRUPTED:
+ /* I/O event: return to caller's poll() loop */
+ return 1 + gnutls_record_get_direction (session);
+ }
}
+ while (!gnutls_error_is_fatal (val));
+
+#ifdef _WIN32
+ msg_Dbg (tls, "Winsock error %d", WSAGetLastError ());
+#endif
+ msg_Err (tls, "TLS handshake error: %s", gnutls_strerror (val));
+ return -1;
- sys->handshaked = true;
+done:
+ if (alp != NULL)
+ {
+ gnutls_datum_t datum;
+
+ val = gnutls_alpn_get_selected_protocol (session, &datum);
+ if (val == 0)
+ {
+ if (memchr (datum.data, 0, datum.size) != NULL)
+ return -1; /* Other end is doing something fishy?! */
+
+ *alp = strndup ((char *)datum.data, datum.size);
+ if (unlikely(*alp == NULL))
+ return -1;
+ }
+ else
+ *alp = NULL;
+ }
return 0;
}
-
-typedef struct
+/**
+ * Terminates TLS session and releases session data.
+ * You still have to close the socket yourself.
+ */
+static void gnutls_SessionClose (vlc_tls_t *tls)
{
- int flag;
- const char *msg;
-} error_msg_t;
+ gnutls_session_t session = tls->sys;
-static const error_msg_t cert_errors[] =
-{
- { GNUTLS_CERT_INVALID,
- "Certificate could not be verified" },
- { GNUTLS_CERT_REVOKED,
- "Certificate was revoked" },
- { GNUTLS_CERT_SIGNER_NOT_FOUND,
- "Certificate's signer was not found" },
- { GNUTLS_CERT_SIGNER_NOT_CA,
- "Certificate's signer is not a CA" },
- { GNUTLS_CERT_INSECURE_ALGORITHM,
- "Insecure certificate signature algorithm" },
- { GNUTLS_CERT_NOT_ACTIVATED,
- "Certificate is not yet activated" },
- { GNUTLS_CERT_EXPIRED,
- "Certificate has expired" },
- { 0, NULL }
-};
+ if (tls->sock.p_sys != NULL)
+ gnutls_bye (session, GNUTLS_SHUT_WR);
+ gnutls_deinit (session);
+}
-static int gnutls_HandshakeAndValidate (vlc_tls_t *session)
+static int gnutls_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *tls,
+ int fd, const char *hostname,
+ const char *const *alpn)
{
- vlc_tls_sys_t *sys = session->sys;
+ int val = gnutls_SessionOpen (tls, GNUTLS_CLIENT, crd->sys, fd, alpn);
+ if (val != VLC_SUCCESS)
+ return val;
+
+ gnutls_session_t session = tls->sys;
+
+ /* minimum DH prime bits */
+ gnutls_dh_set_prime_bits (session, 1024);
+
+ if (likely(hostname != NULL))
+ /* fill Server Name Indication */
+ gnutls_server_name_set (session, GNUTLS_NAME_DNS,
+ hostname, strlen (hostname));
- int val = gnutls_ContinueHandshake (session);
+ return VLC_SUCCESS;
+}
+
+static int gnutls_ClientHandshake (vlc_tls_t *tls, const char *host,
+ const char *service, char **restrict alp)
+{
+ int val = gnutls_ContinueHandshake (tls, alp);
if (val)
return val;
/* certificates chain verification */
+ gnutls_session_t session = tls->sys;
unsigned status;
- val = gnutls_certificate_verify_peers2 (sys->session, &status);
+ val = gnutls_certificate_verify_peers3 (session, host, &status);
if (val)
{
- msg_Err (session, "Certificate verification failed: %s",
+ msg_Err (tls, "Certificate verification error: %s",
gnutls_strerror (val));
+failure:
+ gnutls_bye (session, GNUTLS_SHUT_RDWR);
return -1;
}
- if (status)
- {
- msg_Err (session, "TLS session: access denied (status 0x%X)", status);
- for (const error_msg_t *e = cert_errors; e->flag; e++)
- {
- if (status & e->flag)
- {
- msg_Err (session, "%s", e->msg);
- status &= ~e->flag;
- }
- }
-
- if (status)
- msg_Err (session,
- "unknown certificate error (you found a bug in VLC)");
- return -1;
+ if (status == 0)
+ { /* Good certificate */
+success:
+ tls->sock.p_sys = tls;
+ return 0;
}
- /* certificate (host)name verification */
- const gnutls_datum_t *data;
- data = gnutls_certificate_get_peers (sys->session, &(unsigned){0});
- if (data == NULL)
- {
- msg_Err (session, "Peer certificate not available");
- return -1;
- }
+ /* Bad certificate */
+ gnutls_datum_t desc;
- gnutls_x509_crt_t cert;
- val = gnutls_x509_crt_init (&cert);
- if (val)
+ if (gnutls_certificate_verification_status_print(status,
+ gnutls_certificate_type_get (session), &desc, 0) == 0)
{
- msg_Err (session, "X.509 fatal error: %s", gnutls_strerror (val));
- return -1;
+ msg_Err (tls, "Certificate verification failure: %s", desc.data);
+ gnutls_free (desc.data);
}
- val = gnutls_x509_crt_import (cert, data, GNUTLS_X509_FMT_DER);
- if (val)
+ status &= ~GNUTLS_CERT_INVALID; /* always set / catch-all error */
+ status &= ~GNUTLS_CERT_SIGNER_NOT_FOUND; /* unknown CA */
+ status &= ~GNUTLS_CERT_UNEXPECTED_OWNER; /* mismatched hostname */
+
+ if (status != 0 || host == NULL)
+ goto failure; /* Really bad certificate */
+
+ /* Look up mismatching certificate in store */
+ const gnutls_datum_t *datum;
+ unsigned count;
+
+ datum = gnutls_certificate_get_peers (session, &count);
+ if (datum == NULL || count == 0)
{
- msg_Err (session, "Certificate import error: %s",
- gnutls_strerror (val));
- goto error;
+ msg_Err (tls, "Peer certificate not available");
+ goto failure;
}
- if (sys->hostname != NULL
- && !gnutls_x509_crt_check_hostname (cert, sys->hostname))
+ msg_Dbg (tls, "%u certificate(s) in the list", count);
+ val = gnutls_verify_stored_pubkey (NULL, NULL, host, service,
+ GNUTLS_CRT_X509, datum, 0);
+ const char *msg;
+ switch (val)
{
- msg_Err (session, "Certificate does not match \"%s\"", sys->hostname);
- goto error;
+ case 0:
+ msg_Dbg (tls, "certificate key match for %s", host);
+ goto success;
+ case GNUTLS_E_NO_CERTIFICATE_FOUND:
+ msg_Dbg (tls, "no known certificates for %s", host);
+ msg = N_("However the security certificate presented by the "
+ "server is unknown and could not be authenticated by any "
+ "trusted Certificate Authority.");
+ break;
+ case GNUTLS_E_CERTIFICATE_KEY_MISMATCH:
+ msg_Dbg (tls, "certificate keys mismatch for %s", host);
+ msg = N_("However the security certificate presented by the "
+ "server changed since the previous visit and was not "
+ "authenticated by any trusted Certificate Authority. ");
+ break;
+ default:
+ msg_Err (tls, "certificate key match error for %s: %s", host,
+ gnutls_strerror (val));
+ goto failure;
}
- gnutls_x509_crt_deinit (cert);
- msg_Dbg (session, "TLS/X.509 certificate verified");
- return 0;
-
-error:
- gnutls_x509_crt_deinit (cert);
- return -1;
-}
+ if (dialog_Question (tls, _("Insecure site"),
+ _("You attempted to reach %s. %s\n"
+ "This problem may be stem from an attempt to breach your security, "
+ "compromise your privacy, or a configuration error.\n\n"
+ "If in doubt, abort now.\n"),
+ _("Abort"), _("View certificate"), NULL,
+ vlc_gettext (msg), host) != 2)
+ goto failure;
-static int
-gnutls_SessionPrioritize (vlc_object_t *obj, gnutls_session_t session)
-{
- char *priorities = var_InheritString (obj, "gnutls-priorities");
- if (unlikely(priorities == NULL))
- return VLC_ENOMEM;
+ gnutls_x509_crt_t cert;
- const char *errp;
- int val = gnutls_priority_set_direct (session, priorities, &errp);
- if (val < 0)
+ if (gnutls_x509_crt_init (&cert))
+ goto failure;
+ if (gnutls_x509_crt_import (cert, datum, GNUTLS_X509_FMT_DER)
+ || gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE, &desc))
{
- msg_Err (obj, "cannot set TLS priorities \"%s\": %s", errp,
- gnutls_strerror (val));
- val = VLC_EGENERIC;
+ gnutls_x509_crt_deinit (cert);
+ goto failure;
}
- else
- val = VLC_SUCCESS;
- free (priorities);
- return val;
-}
-
-
-/**
- * TLS credentials private data
- */
-struct vlc_tls_creds_sys
-{
- gnutls_certificate_credentials_t x509_cred;
- gnutls_dh_params_t dh_params; /* XXX: used for server only */
- int (*handshake) (vlc_tls_t *); /* XXX: useful for server only */
-};
-
-
-/**
- * Terminates TLS session and releases session data.
- * You still have to close the socket yourself.
- */
-static void gnutls_SessionClose (vlc_tls_creds_t *crd, vlc_tls_t *session)
-{
- vlc_tls_sys_t *sys = session->sys;
+ gnutls_x509_crt_deinit (cert);
- if (sys->handshaked)
- gnutls_bye (sys->session, GNUTLS_SHUT_WR);
- gnutls_deinit (sys->session);
+ val = dialog_Question (tls, _("Insecure site"),
+ _("This is the certificate presented by %s:\n%s\n\n"
+ "If in doubt, abort now.\n"),
+ _("Abort"), _("Accept 24 hours"),
+ _("Accept permanently"), host, desc.data);
+ gnutls_free (desc.data);
- free (sys->hostname);
- free (sys);
- (void) crd;
+ time_t expiry = 0;
+ switch (val)
+ {
+ case 2:
+ time (&expiry);
+ expiry += 24 * 60 * 60;
+ case 3:
+ val = gnutls_store_pubkey (NULL, NULL, host, service,
+ GNUTLS_CRT_X509, datum, expiry, 0);
+ if (val)
+ msg_Err (tls, "cannot store X.509 certificate: %s",
+ gnutls_strerror (val));
+ goto success;
+ }
+ goto failure;
}
-
/**
- * Initializes a server-side TLS session.
+ * Initializes a client-side TLS credentials.
*/
-static int gnutls_SessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
- int type, int fd)
+static int OpenClient (vlc_tls_creds_t *crd)
{
- vlc_tls_sys_t *sys = malloc (sizeof (*session->sys));
- if (unlikely(sys == NULL))
- return VLC_ENOMEM;
+ gnutls_certificate_credentials_t x509;
- session->sys = sys;
- session->sock.p_sys = session;
- session->sock.pf_send = gnutls_Send;
- session->sock.pf_recv = gnutls_Recv;
- session->handshake = crd->sys->handshake;
- sys->handshaked = false;
- sys->hostname = NULL;
+ if (gnutls_Init (VLC_OBJECT(crd)))
+ return VLC_EGENERIC;
- int val = gnutls_init (&sys->session, type);
+ int val = gnutls_certificate_allocate_credentials (&x509);
if (val != 0)
{
- msg_Err (session, "cannot initialize TLS session: %s",
+ msg_Err (crd, "cannot allocate credentials: %s",
gnutls_strerror (val));
- free (sys);
+ gnutls_Deinit ();
return VLC_EGENERIC;
}
- if (gnutls_SessionPrioritize (VLC_OBJECT (crd), sys->session))
- goto error;
-
- val = gnutls_credentials_set (sys->session, GNUTLS_CRD_CERTIFICATE,
- crd->sys->x509_cred);
+ val = gnutls_certificate_set_x509_system_trust (x509);
if (val < 0)
- {
- msg_Err (session, "cannot set TLS session credentials: %s",
+ msg_Err (crd, "cannot load trusted Certificate Authorities: %s",
gnutls_strerror (val));
- goto error;
- }
-
- gnutls_transport_set_ptr (sys->session,
- (gnutls_transport_ptr_t)(intptr_t)fd);
- return VLC_SUCCESS;
+ else
+ msg_Dbg (crd, "loaded %d trusted CAs", val);
-error:
- gnutls_SessionClose (crd, session);
- return VLC_EGENERIC;
-}
+ gnutls_certificate_set_verify_flags (x509,
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
-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;
+ crd->sys = x509;
+ crd->open = gnutls_ClientSessionOpen;
+ crd->handshake = gnutls_ClientHandshake;
+ crd->close = gnutls_SessionClose;
- 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)
+static void CloseClient (vlc_tls_creds_t *crd)
{
- 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;
- }
+ gnutls_certificate_credentials_t x509 = crd->sys;
- return VLC_SUCCESS;
-error:
- gnutls_SessionClose (crd, session);
- return VLC_EGENERIC;
+ gnutls_certificate_free_credentials (x509);
+ gnutls_Deinit ();
}
-
+#ifdef ENABLE_SOUT
/**
- * Adds one or more Certificate Authorities to the trusted set.
- *
- * @param path (UTF-8) path to an X.509 certificates list.
- *
- * @return -1 on error, 0 on success.
+ * Server-side TLS credentials private data
*/
-static int gnutls_AddCA (vlc_tls_creds_t *crd, const char *path)
+typedef struct vlc_tls_creds_sys
{
- block_t *block = block_FilePath (path);
- if (block == NULL)
- {
- msg_Err (crd, "cannot read trusted CA from %s: %m", path);
- return VLC_EGENERIC;
- }
-
- gnutls_datum_t d = {
- .data = block->p_buffer,
- .size = block->i_buffer,
- };
-
- int val = gnutls_certificate_set_x509_trust_mem (crd->sys->x509_cred, &d,
- GNUTLS_X509_FMT_PEM);
- block_Release (block);
- if (val < 0)
- {
- msg_Err (crd, "cannot load trusted CA from %s: %s", path,
- gnutls_strerror (val));
- return VLC_EGENERIC;
- }
- msg_Dbg (crd, " %d trusted CA%s added from %s", val, (val != 1) ? "s" : "",
- path);
-
- /* enables peer's certificate verification */
- crd->sys->handshake = gnutls_HandshakeAndValidate;
- return VLC_SUCCESS;
-}
-
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_dh_params_t dh_params;
+} vlc_tls_creds_sys_t;
/**
- * Adds a Certificates Revocation List to be sent to TLS clients.
- *
- * @param path (UTF-8) path of the CRL file.
- *
- * @return -1 on error, 0 on success.
+ * Initializes a server-side TLS session.
*/
-static int gnutls_AddCRL (vlc_tls_creds_t *crd, const char *path)
+static int gnutls_ServerSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *tls,
+ int fd, const char *hostname,
+ const char *const *alpn)
{
- block_t *block = block_FilePath (path);
- if (block == NULL)
- {
- msg_Err (crd, "cannot read CRL from %s: %m", path);
- return VLC_EGENERIC;
- }
-
- gnutls_datum_t d = {
- .data = block->p_buffer,
- .size = block->i_buffer,
- };
+ vlc_tls_creds_sys_t *sys = crd->sys;
- int val = gnutls_certificate_set_x509_crl_mem (crd->sys->x509_cred, &d,
- GNUTLS_X509_FMT_PEM);
- block_Release (block);
- if (val < 0)
- {
- msg_Err (crd, "cannot add CRL (%s): %s", path, gnutls_strerror (val));
- return VLC_EGENERIC;
- }
- msg_Dbg (crd, "%d CRL%s added from %s", val, (val != 1) ? "s" : "", path);
- return VLC_SUCCESS;
+ assert (hostname == NULL);
+ return gnutls_SessionOpen (tls, GNUTLS_SERVER, sys->x509_cred, fd, alpn);
}
+static int gnutls_ServerHandshake (vlc_tls_t *tls, const char *host,
+ const char *service, char **restrict alp)
+{
+ int val = gnutls_ContinueHandshake (tls, alp);
+ if (val == 0)
+ tls->sock.p_sys = tls;
+
+ (void) host; (void) service;
+ return val;
+}
/**
* Allocates a whole server's TLS credentials.
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_ServerSessionOpen;
- crd->close = gnutls_SessionClose;
- /* No certificate validation by default */
- sys->handshake = gnutls_ContinueHandshake;
+ {
+ gnutls_Deinit ();
+ return VLC_ENOMEM;
+ }
/* Sets server's credentials */
val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
{
msg_Err (crd, "cannot allocate credentials: %s",
gnutls_strerror (val));
- goto error;
+ free (sys);
+ gnutls_Deinit ();
+ return VLC_ENOMEM;
}
block_t *certblock = block_FilePath (cert);
if (certblock == NULL)
{
- msg_Err (crd, "cannot read certificate chain from %s: %m", cert);
- return VLC_EGENERIC;
+ msg_Err (crd, "cannot read certificate chain from %s: %s", cert,
+ vlc_strerror_c(errno));
+ goto error;
}
block_t *keyblock = block_FilePath (key);
if (keyblock == NULL)
{
- msg_Err (crd, "cannot read private key from %s: %m", key);
+ msg_Err (crd, "cannot read private key from %s: %s", key,
+ vlc_strerror_c(errno));
block_Release (certblock);
- return VLC_EGENERIC;
+ goto error;
}
gnutls_datum_t pub = {
gnutls_strerror (val));
}
+ crd->sys = sys;
+ crd->open = gnutls_ServerSessionOpen;
+ crd->handshake = gnutls_ServerHandshake;
+ crd->close = gnutls_SessionClose;
+
return VLC_SUCCESS;
error:
+ gnutls_certificate_free_credentials (sys->x509_cred);
free (sys);
- gnutls_Deinit (VLC_OBJECT(crd));
+ gnutls_Deinit ();
return VLC_EGENERIC;
}
gnutls_certificate_free_credentials (sys->x509_cred);
gnutls_dh_params_deinit (sys->dh_params);
free (sys);
-
- gnutls_Deinit (VLC_OBJECT(crd));
+ gnutls_Deinit ();
}
+#endif
-/**
- * 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);
+#define PRIORITIES_TEXT N_("TLS cipher priorities")
+#define PRIORITIES_LONGTEXT N_("Ciphers, key exchange methods, " \
+ "hash functions and compression methods can be selected. " \
+ "Refer to GNU TLS documentation for detailed syntax.")
+static const char *const priorities_values[] = {
+ "PERFORMANCE",
+ "NORMAL",
+ "SECURE128",
+ "SECURE256",
+ "EXPORT",
+};
+static const char *const priorities_text[] = {
+ N_("Performance (prioritize faster ciphers)"),
+ N_("Normal"),
+ N_("Secure 128-bits (exclude 256-bits ciphers)"),
+ N_("Secure 256-bits (prioritize 256-bits ciphers)"),
+ N_("Export (include insecure ciphers)"),
+};
- gnutls_Deinit (VLC_OBJECT(crd));
-}
+vlc_module_begin ()
+ set_shortname( "GNU TLS" )
+ set_description( N_("GNU TLS transport layer security") )
+ set_capability( "tls client", 1 )
+ set_callbacks( OpenClient, CloseClient )
+ set_category( CAT_ADVANCED )
+ set_subcategory( SUBCAT_ADVANCED_NETWORK )
+ add_string ("gnutls-priorities", "NORMAL", PRIORITIES_TEXT,
+ PRIORITIES_LONGTEXT, false)
+ change_string_list (priorities_values, priorities_text)
+#ifdef ENABLE_SOUT
+ add_submodule ()
+ set_description( N_("GNU TLS server") )
+ set_capability( "tls server", 1 )
+ set_category( CAT_ADVANCED )
+ set_subcategory( SUBCAT_ADVANCED_NETWORK )
+ set_callbacks( OpenServer, CloseServer )
+#endif
+vlc_module_end ()