1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2004-2012 Rémi Denis-Courmont
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Öesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 /*****************************************************************************
23 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
35 #include <vlc_block.h>
36 #include <vlc_dialog.h>
38 #include <gnutls/gnutls.h>
39 #include <gnutls/x509.h>
40 #if (GNUTLS_VERSION_NUMBER < 0x030014)
41 # define gnutls_certificate_set_x509_system_trust(c) \
42 (c, GNUTLS_E_UNIMPLEMENTED_FEATURE)
47 /*****************************************************************************
49 *****************************************************************************/
50 static int OpenClient (vlc_tls_creds_t *);
51 static void CloseClient (vlc_tls_creds_t *);
52 static int OpenServer (vlc_tls_creds_t *, const char *, const char *);
53 static void CloseServer (vlc_tls_creds_t *);
55 #define PRIORITIES_TEXT N_("TLS cipher priorities")
56 #define PRIORITIES_LONGTEXT N_("Ciphers, key exchange methods, " \
57 "hash functions and compression methods can be selected. " \
58 "Refer to GNU TLS documentation for detailed syntax.")
59 static const char *const priorities_values[] = {
66 static const char *const priorities_text[] = {
67 N_("Performance (prioritize faster ciphers)"),
69 N_("Secure 128-bits (exclude 256-bits ciphers)"),
70 N_("Secure 256-bits (prioritize 256-bits ciphers)"),
71 N_("Export (include insecure ciphers)"),
75 set_shortname( "GNU TLS" )
76 set_description( N_("GNU TLS transport layer security") )
77 set_capability( "tls client", 1 )
78 set_callbacks( OpenClient, CloseClient )
79 set_category( CAT_ADVANCED )
80 set_subcategory( SUBCAT_ADVANCED_MISC )
83 set_description( N_("GNU TLS server") )
84 set_capability( "tls server", 1 )
85 set_category( CAT_ADVANCED )
86 set_subcategory( SUBCAT_ADVANCED_MISC )
87 set_callbacks( OpenServer, CloseServer )
89 add_string ("gnutls-priorities", "NORMAL", PRIORITIES_TEXT,
90 PRIORITIES_LONGTEXT, false)
91 change_string_list (priorities_values, priorities_text)
94 static vlc_mutex_t gnutls_mutex = VLC_STATIC_MUTEX;
97 * Initializes GnuTLS with proper locking.
98 * @return VLC_SUCCESS on success, a VLC error code otherwise.
100 static int gnutls_Init (vlc_object_t *p_this)
102 int ret = VLC_EGENERIC;
104 vlc_mutex_lock (&gnutls_mutex);
105 if (gnutls_global_init ())
107 msg_Err (p_this, "cannot initialize GnuTLS");
111 const char *psz_version = gnutls_check_version ("2.6.6");
112 if (psz_version == NULL)
114 msg_Err (p_this, "unsupported GnuTLS version");
115 gnutls_global_deinit ();
119 msg_Dbg (p_this, "GnuTLS v%s initialized", psz_version);
123 vlc_mutex_unlock (&gnutls_mutex);
129 * Deinitializes GnuTLS.
131 static void gnutls_Deinit (vlc_object_t *p_this)
133 vlc_mutex_lock (&gnutls_mutex);
135 gnutls_global_deinit ();
136 msg_Dbg (p_this, "GnuTLS deinitialized");
137 vlc_mutex_unlock (&gnutls_mutex);
141 static int gnutls_Error (vlc_object_t *obj, int val)
147 WSASetLastError (WSAEWOULDBLOCK);
153 case GNUTLS_E_INTERRUPTED:
155 WSASetLastError (WSAEINTR);
162 msg_Err (obj, "%s", gnutls_strerror (val));
164 if (!gnutls_error_is_fatal (val))
165 msg_Err (obj, "Error above should be handled");
168 WSASetLastError (WSAECONNRESET);
175 #define gnutls_Error(o, val) gnutls_Error(VLC_OBJECT(o), val)
179 gnutls_session_t session;
185 * Sends data through a TLS session.
187 static int gnutls_Send (void *opaque, const void *buf, size_t length)
189 vlc_tls_t *session = opaque;
190 vlc_tls_sys_t *sys = session->sys;
192 int val = gnutls_record_send (sys->session, buf, length);
193 return (val < 0) ? gnutls_Error (session, val) : val;
198 * Receives data through a TLS session.
200 static int gnutls_Recv (void *opaque, void *buf, size_t length)
202 vlc_tls_t *session = opaque;
203 vlc_tls_sys_t *sys = session->sys;
205 int val = gnutls_record_recv (sys->session, buf, length);
206 return (val < 0) ? gnutls_Error (session, val) : val;
211 * Starts or continues the TLS handshake.
213 * @return -1 on fatal error, 0 on successful handshake completion,
214 * 1 if more would-be blocking recv is needed,
215 * 2 if more would-be blocking send is required.
217 static int gnutls_ContinueHandshake (vlc_tls_t *session, const char *host,
220 vlc_tls_sys_t *sys = session->sys;
226 val = gnutls_handshake (sys->session);
227 if ((val == GNUTLS_E_AGAIN) || (val == GNUTLS_E_INTERRUPTED))
228 return 1 + gnutls_record_get_direction (sys->session);
233 msg_Dbg (session, "Winsock error %d", WSAGetLastError ());
235 msg_Err (session, "TLS handshake error: %s", gnutls_strerror (val));
239 sys->handshaked = true;
240 (void) host; (void) service;
246 * Looks up certificate in known hosts data base.
247 * @return 0 on success, -1 on failure.
249 static int gnutls_CertSearch (vlc_tls_t *obj, const char *host,
250 const gnutls_datum_t *restrict datum)
252 assert (host != NULL);
254 if (dialog_Question (obj, N_("Insecure site"),
255 N_("You attempted to reach %s, but security certificate presented by "
256 "the server could not be verified."
257 "This problem may be caused by a configuration error "
258 "on the server or by a serious breach of network security.\n\n"
259 "If in doubt, abort now.\n"),
260 N_("Abort"), N_("View certificate"), NULL, host) != 2)
263 gnutls_x509_crt_t cert;
266 if (gnutls_x509_crt_init (&cert))
268 if (gnutls_x509_crt_import (cert, datum, GNUTLS_X509_FMT_DER)
269 || gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE, &desc))
271 gnutls_x509_crt_deinit (cert);
274 gnutls_x509_crt_deinit (cert);
276 int val = dialog_Question (obj, N_("Insecure site"),
277 N_("This is the certificate presented by %s:\n%s\n\n"
278 "If in doubt, abort now.\n"),
279 N_("Abort"), N_("Proceed anyway"), NULL,
281 gnutls_free (desc.data);
283 return (val == 2) ? 0 : -1;
294 { GNUTLS_CERT_INVALID,
295 "Certificate could not be verified", false },
296 { GNUTLS_CERT_REVOKED,
297 "Certificate was revoked", true },
298 { GNUTLS_CERT_SIGNER_NOT_FOUND,
299 "Certificate's signer was not found", false },
300 { GNUTLS_CERT_SIGNER_NOT_CA,
301 "Certificate's signer is not a CA", true },
302 { GNUTLS_CERT_INSECURE_ALGORITHM,
303 "Insecure certificate signature algorithm", true },
304 { GNUTLS_CERT_NOT_ACTIVATED,
305 "Certificate is not yet activated", true },
306 { GNUTLS_CERT_EXPIRED,
307 "Certificate has expired", true },
311 static int gnutls_HandshakeAndValidate (vlc_tls_t *session, const char *host,
314 vlc_tls_sys_t *sys = session->sys;
316 int val = gnutls_ContinueHandshake (session, host, service);
320 /* certificates chain verification */
323 val = gnutls_certificate_verify_peers2 (sys->session, &status);
326 msg_Err (session, "Certificate verification error: %s",
327 gnutls_strerror (val));
333 msg_Err (session, "Certificate verification failure:");
334 for (size_t i = 0; i < sizeof (cert_errs) / sizeof (cert_errs[0]); i++)
335 if (status & cert_errs[i].flag)
337 msg_Err (session, " * %s", cert_errs[i].msg);
338 status &= ~cert_errs[i].flag;
339 if (cert_errs[i].strict)
345 msg_Err (session, " * Unknown verification error 0x%04X", status);
351 /* certificate (host)name verification */
352 const gnutls_datum_t *data;
354 data = gnutls_certificate_get_peers (sys->session, &count);
355 if (data == NULL || count == 0)
357 msg_Err (session, "Peer certificate not available");
360 msg_Dbg (session, "%u certificate(s) in the list", count);
362 if (val || host == NULL)
364 if (status && gnutls_CertSearch (session, host, data))
367 gnutls_x509_crt_t cert;
368 val = gnutls_x509_crt_init (&cert);
371 msg_Err (session, "X.509 fatal error: %s", gnutls_strerror (val));
375 val = gnutls_x509_crt_import (cert, data, GNUTLS_X509_FMT_DER);
378 msg_Err (session, "Certificate import error: %s",
379 gnutls_strerror (val));
383 val = !gnutls_x509_crt_check_hostname (cert, host);
386 msg_Err (session, "Certificate does not match \"%s\"", host);
387 val = gnutls_CertSearch (session, host, data);
390 gnutls_x509_crt_init (&cert);
395 gnutls_SessionPrioritize (vlc_object_t *obj, gnutls_session_t session)
397 char *priorities = var_InheritString (obj, "gnutls-priorities");
398 if (unlikely(priorities == NULL))
402 int val = gnutls_priority_set_direct (session, priorities, &errp);
405 msg_Err (obj, "cannot set TLS priorities \"%s\": %s", errp,
406 gnutls_strerror (val));
417 * TLS credentials private data
419 struct vlc_tls_creds_sys
421 gnutls_certificate_credentials_t x509_cred;
422 gnutls_dh_params_t dh_params; /* XXX: used for server only */
423 int (*handshake) (vlc_tls_t *, const char *, const char *);
424 /* ^^ XXX: useful for server only */
429 * Terminates TLS session and releases session data.
430 * You still have to close the socket yourself.
432 static void gnutls_SessionClose (vlc_tls_creds_t *crd, vlc_tls_t *session)
434 vlc_tls_sys_t *sys = session->sys;
437 gnutls_bye (sys->session, GNUTLS_SHUT_WR);
438 gnutls_deinit (sys->session);
446 * Initializes a server-side TLS session.
448 static int gnutls_SessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
451 vlc_tls_sys_t *sys = malloc (sizeof (*session->sys));
452 if (unlikely(sys == NULL))
456 session->sock.p_sys = session;
457 session->sock.pf_send = gnutls_Send;
458 session->sock.pf_recv = gnutls_Recv;
459 session->handshake = crd->sys->handshake;
460 sys->handshaked = false;
462 int val = gnutls_init (&sys->session, type);
465 msg_Err (session, "cannot initialize TLS session: %s",
466 gnutls_strerror (val));
471 if (gnutls_SessionPrioritize (VLC_OBJECT (crd), sys->session))
474 val = gnutls_credentials_set (sys->session, GNUTLS_CRD_CERTIFICATE,
475 crd->sys->x509_cred);
478 msg_Err (session, "cannot set TLS session credentials: %s",
479 gnutls_strerror (val));
483 gnutls_transport_set_ptr (sys->session,
484 (gnutls_transport_ptr_t)(intptr_t)fd);
488 gnutls_SessionClose (crd, session);
492 static int gnutls_ServerSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
493 int fd, const char *hostname)
495 int val = gnutls_SessionOpen (crd, session, GNUTLS_SERVER, fd);
496 if (val != VLC_SUCCESS)
499 if (session->handshake == gnutls_HandshakeAndValidate)
500 gnutls_certificate_server_set_request (session->sys->session,
501 GNUTLS_CERT_REQUIRE);
502 assert (hostname == NULL);
506 static int gnutls_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
507 int fd, const char *hostname)
509 int val = gnutls_SessionOpen (crd, session, GNUTLS_CLIENT, fd);
510 if (val != VLC_SUCCESS)
513 vlc_tls_sys_t *sys = session->sys;
515 /* minimum DH prime bits */
516 gnutls_dh_set_prime_bits (sys->session, 1024);
518 if (likely(hostname != NULL))
519 /* fill Server Name Indication */
520 gnutls_server_name_set (sys->session, GNUTLS_NAME_DNS,
521 hostname, strlen (hostname));
528 * Adds one or more Certificate Authorities to the trusted set.
530 * @param path (UTF-8) path to an X.509 certificates list.
532 * @return -1 on error, 0 on success.
534 static int gnutls_AddCA (vlc_tls_creds_t *crd, const char *path)
536 block_t *block = block_FilePath (path);
539 msg_Err (crd, "cannot read trusted CA from %s: %m", path);
544 .data = block->p_buffer,
545 .size = block->i_buffer,
548 int val = gnutls_certificate_set_x509_trust_mem (crd->sys->x509_cred, &d,
549 GNUTLS_X509_FMT_PEM);
550 block_Release (block);
553 msg_Err (crd, "cannot load trusted CA from %s: %s", path,
554 gnutls_strerror (val));
557 msg_Dbg (crd, " %d trusted CA%s added from %s", val, (val != 1) ? "s" : "",
560 /* enables peer's certificate verification */
561 crd->sys->handshake = gnutls_HandshakeAndValidate;
567 * Adds a Certificates Revocation List to be sent to TLS clients.
569 * @param path (UTF-8) path of the CRL file.
571 * @return -1 on error, 0 on success.
573 static int gnutls_AddCRL (vlc_tls_creds_t *crd, const char *path)
575 block_t *block = block_FilePath (path);
578 msg_Err (crd, "cannot read CRL from %s: %m", path);
583 .data = block->p_buffer,
584 .size = block->i_buffer,
587 int val = gnutls_certificate_set_x509_crl_mem (crd->sys->x509_cred, &d,
588 GNUTLS_X509_FMT_PEM);
589 block_Release (block);
592 msg_Err (crd, "cannot add CRL (%s): %s", path, gnutls_strerror (val));
595 msg_Dbg (crd, "%d CRL%s added from %s", val, (val != 1) ? "s" : "", path);
601 * Allocates a whole server's TLS credentials.
603 static int OpenServer (vlc_tls_creds_t *crd, const char *cert, const char *key)
607 if (gnutls_Init (VLC_OBJECT(crd)))
610 vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
611 if (unlikely(sys == NULL))
615 crd->add_CA = gnutls_AddCA;
616 crd->add_CRL = gnutls_AddCRL;
617 crd->open = gnutls_ServerSessionOpen;
618 crd->close = gnutls_SessionClose;
619 /* No certificate validation by default */
620 sys->handshake = gnutls_ContinueHandshake;
622 /* Sets server's credentials */
623 val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
626 msg_Err (crd, "cannot allocate credentials: %s",
627 gnutls_strerror (val));
631 block_t *certblock = block_FilePath (cert);
632 if (certblock == NULL)
634 msg_Err (crd, "cannot read certificate chain from %s: %m", cert);
638 block_t *keyblock = block_FilePath (key);
639 if (keyblock == NULL)
641 msg_Err (crd, "cannot read private key from %s: %m", key);
642 block_Release (certblock);
646 gnutls_datum_t pub = {
647 .data = certblock->p_buffer,
648 .size = certblock->i_buffer,
650 .data = keyblock->p_buffer,
651 .size = keyblock->i_buffer,
654 val = gnutls_certificate_set_x509_key_mem (sys->x509_cred, &pub, &priv,
655 GNUTLS_X509_FMT_PEM);
656 block_Release (keyblock);
657 block_Release (certblock);
660 msg_Err (crd, "cannot load X.509 key: %s", gnutls_strerror (val));
661 gnutls_certificate_free_credentials (sys->x509_cred);
666 * - support other cipher suites
668 val = gnutls_dh_params_init (&sys->dh_params);
671 const gnutls_datum_t data = {
672 .data = (unsigned char *)dh_params,
673 .size = sizeof (dh_params) - 1,
676 val = gnutls_dh_params_import_pkcs3 (sys->dh_params, &data,
677 GNUTLS_X509_FMT_PEM);
679 gnutls_certificate_set_dh_params (sys->x509_cred,
684 msg_Err (crd, "cannot initialize DHE cipher suites: %s",
685 gnutls_strerror (val));
692 gnutls_Deinit (VLC_OBJECT(crd));
697 * Destroys a TLS server object.
699 static void CloseServer (vlc_tls_creds_t *crd)
701 vlc_tls_creds_sys_t *sys = crd->sys;
703 /* all sessions depending on the server are now deinitialized */
704 gnutls_certificate_free_credentials (sys->x509_cred);
705 gnutls_dh_params_deinit (sys->dh_params);
708 gnutls_Deinit (VLC_OBJECT(crd));
712 * Initializes a client-side TLS credentials.
714 static int OpenClient (vlc_tls_creds_t *crd)
716 if (gnutls_Init (VLC_OBJECT(crd)))
719 vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
720 if (unlikely(sys == NULL))
724 //crd->add_CA = gnutls_AddCA;
725 //crd->add_CRL = gnutls_AddCRL;
726 crd->open = gnutls_ClientSessionOpen;
727 crd->close = gnutls_SessionClose;
728 sys->handshake = gnutls_HandshakeAndValidate;
730 int val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
733 msg_Err (crd, "cannot allocate credentials: %s",
734 gnutls_strerror (val));
738 val = gnutls_certificate_set_x509_system_trust (sys->x509_cred);
740 msg_Err (crd, "cannot load trusted Certificate Authorities: %s",
741 gnutls_strerror (val));
743 msg_Dbg (crd, "loaded %d trusted CAs", val);
745 gnutls_certificate_set_verify_flags (sys->x509_cred,
746 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
751 gnutls_Deinit (VLC_OBJECT(crd));
755 static void CloseClient (vlc_tls_creds_t *crd)
757 vlc_tls_creds_sys_t *sys = crd->sys;
759 gnutls_certificate_free_credentials (sys->x509_cred);
762 gnutls_Deinit (VLC_OBJECT(crd));