1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2004-2014 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 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
34 #include <vlc_block.h>
35 #include <vlc_dialog.h>
37 #include <gnutls/gnutls.h>
38 #include <gnutls/x509.h>
41 #if (GNUTLS_VERSION_NUMBER >= 0x030300)
42 static int gnutls_Init (vlc_object_t *obj)
44 const char *version = gnutls_check_version ("3.3.0");
47 msg_Err (obj, "unsupported GnuTLS version");
50 msg_Dbg (obj, "using GnuTLS version %s", version);
54 # define gnutls_Deinit() (void)0
56 static vlc_mutex_t gnutls_mutex = VLC_STATIC_MUTEX;
59 * Initializes GnuTLS with proper locking.
60 * @return VLC_SUCCESS on success, a VLC error code otherwise.
62 static int gnutls_Init (vlc_object_t *obj)
64 const char *version = gnutls_check_version ("3.1.11");
67 msg_Err (obj, "unsupported GnuTLS version");
70 msg_Dbg (obj, "using GnuTLS version %s", version);
72 if (gnutls_check_version ("3.3.0") == NULL)
76 vlc_mutex_lock (&gnutls_mutex);
77 val = gnutls_global_init ();
78 vlc_mutex_unlock (&gnutls_mutex);
82 msg_Err (obj, "cannot initialize GnuTLS");
90 * Deinitializes GnuTLS.
92 static void gnutls_Deinit (void)
94 vlc_mutex_lock (&gnutls_mutex);
95 gnutls_global_deinit ();
96 vlc_mutex_unlock (&gnutls_mutex);
100 static int gnutls_Error (vlc_object_t *obj, int val)
106 WSASetLastError (WSAEWOULDBLOCK);
112 case GNUTLS_E_INTERRUPTED:
114 WSASetLastError (WSAEINTR);
121 msg_Err (obj, "%s", gnutls_strerror (val));
123 if (!gnutls_error_is_fatal (val))
124 msg_Err (obj, "Error above should be handled");
127 WSASetLastError (WSAECONNRESET);
134 #define gnutls_Error(o, val) gnutls_Error(VLC_OBJECT(o), val)
137 * Sends data through a TLS session.
139 static int gnutls_Send (void *opaque, const void *buf, size_t length)
141 assert (opaque != NULL);
143 vlc_tls_t *tls = opaque;
144 gnutls_session_t session = tls->sys;
146 int val = gnutls_record_send (session, buf, length);
147 return (val < 0) ? gnutls_Error (tls, val) : val;
152 * Receives data through a TLS session.
154 static int gnutls_Recv (void *opaque, void *buf, size_t length)
156 assert (opaque != NULL);
158 vlc_tls_t *tls = opaque;
159 gnutls_session_t session = tls->sys;
161 int val = gnutls_record_recv (session, buf, length);
162 return (val < 0) ? gnutls_Error (tls, val) : val;
165 static int gnutls_SessionOpen (vlc_tls_t *tls, int type,
166 gnutls_certificate_credentials_t x509, int fd,
167 const char *const *alpn)
169 gnutls_session_t session;
173 val = gnutls_init (&session, type);
176 msg_Err (tls, "cannot initialize TLS session: %s",
177 gnutls_strerror (val));
181 char *priorities = var_InheritString (tls, "gnutls-priorities");
182 if (unlikely(priorities == NULL))
185 val = gnutls_priority_set_direct (session, priorities, &errp);
187 msg_Err (tls, "cannot set TLS priorities \"%s\": %s", errp,
188 gnutls_strerror (val));
193 val = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509);
196 msg_Err (tls, "cannot set TLS session credentials: %s",
197 gnutls_strerror (val));
203 gnutls_datum_t *protv = NULL;
206 while (*alpn != NULL)
208 gnutls_datum_t *n = realloc(protv, sizeof (*protv) * (protc + 1));
209 if (unlikely(n == NULL))
216 protv[protc].data = (void *)*alpn;
217 protv[protc].size = strlen(*alpn);
222 val = gnutls_alpn_set_protocols (session, protv, protc, 0);
226 gnutls_transport_set_int (session, fd);
229 tls->sock.p_sys = NULL;
230 tls->sock.pf_send = gnutls_Send;
231 tls->sock.pf_recv = gnutls_Recv;
235 gnutls_deinit (session);
240 * Starts or continues the TLS handshake.
242 * @return -1 on fatal error, 0 on successful handshake completion,
243 * 1 if more would-be blocking recv is needed,
244 * 2 if more would-be blocking send is required.
246 static int gnutls_ContinueHandshake (vlc_tls_t *tls, char **restrict alp)
248 gnutls_session_t session = tls->sys;
256 val = gnutls_handshake (session);
257 msg_Dbg (tls, "TLS handshake: %s", gnutls_strerror (val));
261 case GNUTLS_E_SUCCESS:
264 case GNUTLS_E_INTERRUPTED:
265 /* I/O event: return to caller's poll() loop */
266 return 1 + gnutls_record_get_direction (session);
269 while (!gnutls_error_is_fatal (val));
272 msg_Dbg (tls, "Winsock error %d", WSAGetLastError ());
274 msg_Err (tls, "TLS handshake error: %s", gnutls_strerror (val));
280 gnutls_datum_t datum;
282 val = gnutls_alpn_get_selected_protocol (session, &datum);
285 if (memchr (datum.data, 0, datum.size) != NULL)
286 return -1; /* Other end is doing something fishy?! */
288 *alp = strndup ((char *)datum.data, datum.size);
289 if (unlikely(*alp == NULL))
299 * Terminates TLS session and releases session data.
300 * You still have to close the socket yourself.
302 static void gnutls_SessionClose (vlc_tls_t *tls)
304 gnutls_session_t session = tls->sys;
306 if (tls->sock.p_sys != NULL)
307 gnutls_bye (session, GNUTLS_SHUT_WR);
309 gnutls_deinit (session);
312 static int gnutls_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *tls,
313 int fd, const char *hostname,
314 const char *const *alpn)
316 int val = gnutls_SessionOpen (tls, GNUTLS_CLIENT, crd->sys, fd, alpn);
317 if (val != VLC_SUCCESS)
320 gnutls_session_t session = tls->sys;
322 /* minimum DH prime bits */
323 gnutls_dh_set_prime_bits (session, 1024);
325 if (likely(hostname != NULL))
326 /* fill Server Name Indication */
327 gnutls_server_name_set (session, GNUTLS_NAME_DNS,
328 hostname, strlen (hostname));
333 static int gnutls_ClientHandshake (vlc_tls_t *tls, const char *host,
334 const char *service, char **restrict alp)
336 int val = gnutls_ContinueHandshake (tls, alp);
340 /* certificates chain verification */
341 gnutls_session_t session = tls->sys;
344 val = gnutls_certificate_verify_peers3 (session, host, &status);
347 msg_Err (tls, "Certificate verification error: %s",
348 gnutls_strerror (val));
350 gnutls_bye (session, GNUTLS_SHUT_RDWR);
355 { /* Good certificate */
357 tls->sock.p_sys = tls;
361 /* Bad certificate */
364 if (gnutls_certificate_verification_status_print(status,
365 gnutls_certificate_type_get (session), &desc, 0) == 0)
367 msg_Err (tls, "Certificate verification failure: %s", desc.data);
368 gnutls_free (desc.data);
371 status &= ~GNUTLS_CERT_INVALID; /* always set / catch-all error */
372 status &= ~GNUTLS_CERT_SIGNER_NOT_FOUND; /* unknown CA */
373 status &= ~GNUTLS_CERT_UNEXPECTED_OWNER; /* mismatched hostname */
375 if (status != 0 || host == NULL)
376 goto failure; /* Really bad certificate */
378 /* Look up mismatching certificate in store */
379 const gnutls_datum_t *datum;
382 datum = gnutls_certificate_get_peers (session, &count);
383 if (datum == NULL || count == 0)
385 msg_Err (tls, "Peer certificate not available");
389 msg_Dbg (tls, "%u certificate(s) in the list", count);
390 val = gnutls_verify_stored_pubkey (NULL, NULL, host, service,
391 GNUTLS_CRT_X509, datum, 0);
396 msg_Dbg (tls, "certificate key match for %s", host);
398 case GNUTLS_E_NO_CERTIFICATE_FOUND:
399 msg_Dbg (tls, "no known certificates for %s", host);
400 msg = N_("However the security certificate presented by the "
401 "server is unknown and could not be authenticated by any "
402 "trusted Certificate Authority.");
404 case GNUTLS_E_CERTIFICATE_KEY_MISMATCH:
405 msg_Dbg (tls, "certificate keys mismatch for %s", host);
406 msg = N_("However the security certificate presented by the "
407 "server changed since the previous visit and was not "
408 "authenticated by any trusted Certificate Authority. ");
411 msg_Err (tls, "certificate key match error for %s: %s", host,
412 gnutls_strerror (val));
416 if (dialog_Question (tls, _("Insecure site"),
417 _("You attempted to reach %s. %s\n"
418 "This problem may be stem from an attempt to breach your security, "
419 "compromise your privacy, or a configuration error.\n\n"
420 "If in doubt, abort now.\n"),
421 _("Abort"), _("View certificate"), NULL,
422 vlc_gettext (msg), host) != 2)
425 gnutls_x509_crt_t cert;
427 if (gnutls_x509_crt_init (&cert))
429 if (gnutls_x509_crt_import (cert, datum, GNUTLS_X509_FMT_DER)
430 || gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE, &desc))
432 gnutls_x509_crt_deinit (cert);
435 gnutls_x509_crt_deinit (cert);
437 val = dialog_Question (tls, _("Insecure site"),
438 _("This is the certificate presented by %s:\n%s\n\n"
439 "If in doubt, abort now.\n"),
440 _("Abort"), _("Accept 24 hours"),
441 _("Accept permanently"), host, desc.data);
442 gnutls_free (desc.data);
449 expiry += 24 * 60 * 60;
451 val = gnutls_store_pubkey (NULL, NULL, host, service,
452 GNUTLS_CRT_X509, datum, expiry, 0);
454 msg_Err (tls, "cannot store X.509 certificate: %s",
455 gnutls_strerror (val));
462 * Initializes a client-side TLS credentials.
464 static int OpenClient (vlc_tls_creds_t *crd)
466 gnutls_certificate_credentials_t x509;
468 if (gnutls_Init (VLC_OBJECT(crd)))
471 int val = gnutls_certificate_allocate_credentials (&x509);
474 msg_Err (crd, "cannot allocate credentials: %s",
475 gnutls_strerror (val));
480 val = gnutls_certificate_set_x509_system_trust (x509);
482 msg_Err (crd, "cannot load trusted Certificate Authorities: %s",
483 gnutls_strerror (val));
485 msg_Dbg (crd, "loaded %d trusted CAs", val);
487 gnutls_certificate_set_verify_flags (x509,
488 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
491 crd->open = gnutls_ClientSessionOpen;
492 crd->handshake = gnutls_ClientHandshake;
493 crd->close = gnutls_SessionClose;
498 static void CloseClient (vlc_tls_creds_t *crd)
500 gnutls_certificate_credentials_t x509 = crd->sys;
502 gnutls_certificate_free_credentials (x509);
508 * Server-side TLS credentials private data
510 typedef struct vlc_tls_creds_sys
512 gnutls_certificate_credentials_t x509_cred;
513 gnutls_dh_params_t dh_params;
514 } vlc_tls_creds_sys_t;
517 * Initializes a server-side TLS session.
519 static int gnutls_ServerSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *tls,
520 int fd, const char *hostname,
521 const char *const *alpn)
523 vlc_tls_creds_sys_t *sys = crd->sys;
525 assert (hostname == NULL);
526 return gnutls_SessionOpen (tls, GNUTLS_SERVER, sys->x509_cred, fd, alpn);
529 static int gnutls_ServerHandshake (vlc_tls_t *tls, const char *host,
530 const char *service, char **restrict alp)
532 int val = gnutls_ContinueHandshake (tls, alp);
534 tls->sock.p_sys = tls;
536 (void) host; (void) service;
541 * Allocates a whole server's TLS credentials.
543 static int OpenServer (vlc_tls_creds_t *crd, const char *cert, const char *key)
547 if (gnutls_Init (VLC_OBJECT(crd)))
550 vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
551 if (unlikely(sys == NULL))
557 /* Sets server's credentials */
558 val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
561 msg_Err (crd, "cannot allocate credentials: %s",
562 gnutls_strerror (val));
568 block_t *certblock = block_FilePath (cert);
569 if (certblock == NULL)
571 msg_Err (crd, "cannot read certificate chain from %s: %s", cert,
572 vlc_strerror_c(errno));
576 block_t *keyblock = block_FilePath (key);
577 if (keyblock == NULL)
579 msg_Err (crd, "cannot read private key from %s: %s", key,
580 vlc_strerror_c(errno));
581 block_Release (certblock);
585 gnutls_datum_t pub = {
586 .data = certblock->p_buffer,
587 .size = certblock->i_buffer,
589 .data = keyblock->p_buffer,
590 .size = keyblock->i_buffer,
593 val = gnutls_certificate_set_x509_key_mem (sys->x509_cred, &pub, &priv,
594 GNUTLS_X509_FMT_PEM);
595 block_Release (keyblock);
596 block_Release (certblock);
599 msg_Err (crd, "cannot load X.509 key: %s", gnutls_strerror (val));
600 gnutls_certificate_free_credentials (sys->x509_cred);
605 * - support other cipher suites
607 val = gnutls_dh_params_init (&sys->dh_params);
610 const gnutls_datum_t data = {
611 .data = (unsigned char *)dh_params,
612 .size = sizeof (dh_params) - 1,
615 val = gnutls_dh_params_import_pkcs3 (sys->dh_params, &data,
616 GNUTLS_X509_FMT_PEM);
618 gnutls_certificate_set_dh_params (sys->x509_cred,
623 msg_Err (crd, "cannot initialize DHE cipher suites: %s",
624 gnutls_strerror (val));
628 crd->open = gnutls_ServerSessionOpen;
629 crd->handshake = gnutls_ServerHandshake;
630 crd->close = gnutls_SessionClose;
635 gnutls_certificate_free_credentials (sys->x509_cred);
642 * Destroys a TLS server object.
644 static void CloseServer (vlc_tls_creds_t *crd)
646 vlc_tls_creds_sys_t *sys = crd->sys;
648 /* all sessions depending on the server are now deinitialized */
649 gnutls_certificate_free_credentials (sys->x509_cred);
650 gnutls_dh_params_deinit (sys->dh_params);
656 #define PRIORITIES_TEXT N_("TLS cipher priorities")
657 #define PRIORITIES_LONGTEXT N_("Ciphers, key exchange methods, " \
658 "hash functions and compression methods can be selected. " \
659 "Refer to GNU TLS documentation for detailed syntax.")
660 static const char *const priorities_values[] = {
667 static const char *const priorities_text[] = {
668 N_("Performance (prioritize faster ciphers)"),
670 N_("Secure 128-bits (exclude 256-bits ciphers)"),
671 N_("Secure 256-bits (prioritize 256-bits ciphers)"),
672 N_("Export (include insecure ciphers)"),
676 set_shortname( "GNU TLS" )
677 set_description( N_("GNU TLS transport layer security") )
678 set_capability( "tls client", 1 )
679 set_callbacks( OpenClient, CloseClient )
680 set_category( CAT_ADVANCED )
681 set_subcategory( SUBCAT_ADVANCED_NETWORK )
682 add_string ("gnutls-priorities", "NORMAL", PRIORITIES_TEXT,
683 PRIORITIES_LONGTEXT, false)
684 change_string_list (priorities_values, priorities_text)
687 set_description( N_("GNU TLS server") )
688 set_capability( "tls server", 1 )
689 set_category( CAT_ADVANCED )
690 set_subcategory( SUBCAT_ADVANCED_NETWORK )
691 set_callbacks( OpenServer, CloseServer )