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 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
32 #include <vlc_block.h>
33 #include <vlc_dialog.h>
35 #include <gnutls/gnutls.h>
36 #include <gnutls/x509.h>
39 static vlc_mutex_t gnutls_mutex = VLC_STATIC_MUTEX;
42 * Initializes GnuTLS with proper locking.
43 * @return VLC_SUCCESS on success, a VLC error code otherwise.
45 static int gnutls_Init (vlc_object_t *p_this)
47 int ret = VLC_EGENERIC;
49 vlc_mutex_lock (&gnutls_mutex);
50 if (gnutls_global_init ())
52 msg_Err (p_this, "cannot initialize GnuTLS");
56 const char *psz_version = gnutls_check_version ("3.1.4");
57 if (psz_version == NULL)
59 msg_Err (p_this, "unsupported GnuTLS version");
60 gnutls_global_deinit ();
64 msg_Dbg (p_this, "GnuTLS v%s initialized", psz_version);
68 vlc_mutex_unlock (&gnutls_mutex);
74 * Deinitializes GnuTLS.
76 static void gnutls_Deinit (vlc_object_t *p_this)
78 vlc_mutex_lock (&gnutls_mutex);
80 gnutls_global_deinit ();
81 msg_Dbg (p_this, "GnuTLS deinitialized");
82 vlc_mutex_unlock (&gnutls_mutex);
86 static int gnutls_Error (vlc_object_t *obj, int val)
92 WSASetLastError (WSAEWOULDBLOCK);
98 case GNUTLS_E_INTERRUPTED:
100 WSASetLastError (WSAEINTR);
107 msg_Err (obj, "%s", gnutls_strerror (val));
109 if (!gnutls_error_is_fatal (val))
110 msg_Err (obj, "Error above should be handled");
113 WSASetLastError (WSAECONNRESET);
120 #define gnutls_Error(o, val) gnutls_Error(VLC_OBJECT(o), val)
123 * Sends data through a TLS session.
125 static int gnutls_Send (void *opaque, const void *buf, size_t length)
127 assert (opaque != NULL);
129 vlc_tls_t *tls = opaque;
130 gnutls_session_t session = tls->sys;
132 int val = gnutls_record_send (session, buf, length);
133 return (val < 0) ? gnutls_Error (tls, val) : val;
138 * Receives data through a TLS session.
140 static int gnutls_Recv (void *opaque, void *buf, size_t length)
142 assert (opaque != NULL);
144 vlc_tls_t *tls = opaque;
145 gnutls_session_t session = tls->sys;
147 int val = gnutls_record_recv (session, buf, length);
148 return (val < 0) ? gnutls_Error (tls, val) : val;
151 static int gnutls_SessionOpen (vlc_tls_t *tls, int type,
152 gnutls_certificate_credentials_t x509, int fd)
154 gnutls_session_t session;
158 val = gnutls_init (&session, type);
161 msg_Err (tls, "cannot initialize TLS session: %s",
162 gnutls_strerror (val));
166 char *priorities = var_InheritString (tls, "gnutls-priorities");
167 if (unlikely(priorities == NULL))
170 val = gnutls_priority_set_direct (session, priorities, &errp);
172 msg_Err (tls, "cannot set TLS priorities \"%s\": %s", errp,
173 gnutls_strerror (val));
178 val = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509);
181 msg_Err (tls, "cannot set TLS session credentials: %s",
182 gnutls_strerror (val));
186 gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t)(intptr_t)fd);
189 tls->sock.p_sys = NULL;
190 tls->sock.pf_send = gnutls_Send;
191 tls->sock.pf_recv = gnutls_Recv;
195 gnutls_deinit (session);
200 * Starts or continues the TLS handshake.
202 * @return -1 on fatal error, 0 on successful handshake completion,
203 * 1 if more would-be blocking recv is needed,
204 * 2 if more would-be blocking send is required.
206 static int gnutls_ContinueHandshake (vlc_tls_t *tls)
208 gnutls_session_t session = tls->sys;
216 val = gnutls_handshake (session);
217 msg_Dbg (tls, "TLS handshake: %s", gnutls_strerror (val));
221 case GNUTLS_E_SUCCESS:
224 case GNUTLS_E_INTERRUPTED:
225 /* I/O event: return to caller's poll() loop */
226 return 1 + gnutls_record_get_direction (session);
229 while (!gnutls_error_is_fatal (val));
232 msg_Dbg (tls, "Winsock error %d", WSAGetLastError ());
234 msg_Err (tls, "TLS handshake error: %s", gnutls_strerror (val));
239 * Terminates TLS session and releases session data.
240 * You still have to close the socket yourself.
242 static void gnutls_SessionClose (vlc_tls_t *tls)
244 gnutls_session_t session = tls->sys;
246 if (tls->sock.p_sys != NULL)
247 gnutls_bye (session, GNUTLS_SHUT_WR);
249 gnutls_deinit (session);
252 static int gnutls_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *tls,
253 int fd, const char *hostname)
255 int val = gnutls_SessionOpen (tls, GNUTLS_CLIENT, crd->sys, fd);
256 if (val != VLC_SUCCESS)
259 gnutls_session_t session = tls->sys;
261 /* minimum DH prime bits */
262 gnutls_dh_set_prime_bits (session, 1024);
264 if (likely(hostname != NULL))
265 /* fill Server Name Indication */
266 gnutls_server_name_set (session, GNUTLS_NAME_DNS,
267 hostname, strlen (hostname));
272 static int gnutls_ClientHandshake (vlc_tls_t *tls, const char *host,
275 int val = gnutls_ContinueHandshake (tls);
279 /* certificates chain verification */
280 gnutls_session_t session = tls->sys;
283 val = gnutls_certificate_verify_peers3 (session, host, &status);
286 msg_Err (tls, "Certificate verification error: %s",
287 gnutls_strerror (val));
289 gnutls_bye (session, GNUTLS_SHUT_RDWR);
294 { /* Good certificate */
296 tls->sock.p_sys = tls;
300 /* Bad certificate */
303 if (gnutls_certificate_verification_status_print(status,
304 gnutls_certificate_type_get (session), &desc, 0) == 0)
306 msg_Err (tls, "Certificate verification failure: %s", desc.data);
307 gnutls_free (desc.data);
310 status &= ~GNUTLS_CERT_INVALID; /* always set / catch-all error */
311 status &= ~GNUTLS_CERT_SIGNER_NOT_FOUND; /* unknown CA */
312 status &= ~GNUTLS_CERT_UNEXPECTED_OWNER; /* mismatched hostname */
314 if (status != 0 || host == NULL)
315 goto failure; /* Really bad certificate */
317 /* Look up mismatching certificate in store */
318 const gnutls_datum_t *datum;
321 datum = gnutls_certificate_get_peers (session, &count);
322 if (datum == NULL || count == 0)
324 msg_Err (tls, "Peer certificate not available");
328 msg_Dbg (tls, "%u certificate(s) in the list", count);
329 val = gnutls_verify_stored_pubkey (NULL, NULL, host, service,
330 GNUTLS_CRT_X509, datum, 0);
335 msg_Dbg (tls, "certificate key match for %s", host);
337 case GNUTLS_E_NO_CERTIFICATE_FOUND:
338 msg_Dbg (tls, "no known certificates for %s", host);
339 msg = N_("However the security certificate presented by the "
340 "server is unknown and could not be authenticated by any "
341 "trusted Certificate Authority.");
343 case GNUTLS_E_CERTIFICATE_KEY_MISMATCH:
344 msg_Dbg (tls, "certificate keys mismatch for %s", host);
345 msg = N_("However the security certificate presented by the "
346 "server changed since the previous visit and was not "
347 "authenticated by any trusted Certificate Authority. ");
350 msg_Err (tls, "certificate key match error for %s: %s", host,
351 gnutls_strerror (val));
355 if (dialog_Question (tls, _("Insecure site"),
356 _("You attempted to reach %s. %s\n"
357 "This problem may be stem from an attempt to breach your security, "
358 "compromise your privacy, or a configuration error.\n\n"
359 "If in doubt, abort now.\n"),
360 _("Abort"), _("View certificate"), NULL,
361 vlc_gettext (msg), host) != 2)
364 gnutls_x509_crt_t cert;
366 if (gnutls_x509_crt_init (&cert))
368 if (gnutls_x509_crt_import (cert, datum, GNUTLS_X509_FMT_DER)
369 || gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE, &desc))
371 gnutls_x509_crt_deinit (cert);
374 gnutls_x509_crt_deinit (cert);
376 val = dialog_Question (tls, _("Insecure site"),
377 _("This is the certificate presented by %s:\n%s\n\n"
378 "If in doubt, abort now.\n"),
379 _("Abort"), _("Accept 24 hours"),
380 _("Accept permanently"), host, desc.data);
381 gnutls_free (desc.data);
388 expiry += 24 * 60 * 60;
390 val = gnutls_store_pubkey (NULL, NULL, host, service,
391 GNUTLS_CRT_X509, datum, expiry, 0);
393 msg_Err (tls, "cannot store X.509 certificate: %s",
394 gnutls_strerror (val));
401 * Initializes a client-side TLS credentials.
403 static int OpenClient (vlc_tls_creds_t *crd)
405 gnutls_certificate_credentials_t x509;
407 if (gnutls_Init (VLC_OBJECT(crd)))
410 int val = gnutls_certificate_allocate_credentials (&x509);
413 msg_Err (crd, "cannot allocate credentials: %s",
414 gnutls_strerror (val));
415 gnutls_Deinit (VLC_OBJECT(crd));
419 val = gnutls_certificate_set_x509_system_trust (x509);
421 msg_Err (crd, "cannot load trusted Certificate Authorities: %s",
422 gnutls_strerror (val));
424 msg_Dbg (crd, "loaded %d trusted CAs", val);
426 gnutls_certificate_set_verify_flags (x509,
427 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
430 crd->open = gnutls_ClientSessionOpen;
431 crd->handshake = gnutls_ClientHandshake;
432 crd->close = gnutls_SessionClose;
437 static void CloseClient (vlc_tls_creds_t *crd)
439 gnutls_certificate_credentials_t x509 = crd->sys;
441 gnutls_certificate_free_credentials (x509);
443 gnutls_Deinit (VLC_OBJECT(crd));
448 * Server-side TLS credentials private data
450 typedef struct vlc_tls_creds_sys
452 gnutls_certificate_credentials_t x509_cred;
453 gnutls_dh_params_t dh_params;
454 } vlc_tls_creds_sys_t;
457 * Initializes a server-side TLS session.
459 static int gnutls_ServerSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *tls,
460 int fd, const char *hostname)
462 vlc_tls_creds_sys_t *sys = crd->sys;
464 assert (hostname == NULL);
465 return gnutls_SessionOpen (tls, GNUTLS_SERVER, sys->x509_cred, fd);
468 static int gnutls_ServerHandshake (vlc_tls_t *tls, const char *host,
471 int val = gnutls_ContinueHandshake (tls);
473 tls->sock.p_sys = tls;
475 (void) host; (void) service;
480 * Allocates a whole server's TLS credentials.
482 static int OpenServer (vlc_tls_creds_t *crd, const char *cert, const char *key)
486 if (gnutls_Init (VLC_OBJECT(crd)))
489 vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
490 if (unlikely(sys == NULL))
493 /* Sets server's credentials */
494 val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
497 msg_Err (crd, "cannot allocate credentials: %s",
498 gnutls_strerror (val));
502 block_t *certblock = block_FilePath (cert);
503 if (certblock == NULL)
505 msg_Err (crd, "cannot read certificate chain from %s: %s", cert,
506 vlc_strerror_c(errno));
510 block_t *keyblock = block_FilePath (key);
511 if (keyblock == NULL)
513 msg_Err (crd, "cannot read private key from %s: %s", key,
514 vlc_strerror_c(errno));
515 block_Release (certblock);
519 gnutls_datum_t pub = {
520 .data = certblock->p_buffer,
521 .size = certblock->i_buffer,
523 .data = keyblock->p_buffer,
524 .size = keyblock->i_buffer,
527 val = gnutls_certificate_set_x509_key_mem (sys->x509_cred, &pub, &priv,
528 GNUTLS_X509_FMT_PEM);
529 block_Release (keyblock);
530 block_Release (certblock);
533 msg_Err (crd, "cannot load X.509 key: %s", gnutls_strerror (val));
534 gnutls_certificate_free_credentials (sys->x509_cred);
539 * - support other cipher suites
541 val = gnutls_dh_params_init (&sys->dh_params);
544 const gnutls_datum_t data = {
545 .data = (unsigned char *)dh_params,
546 .size = sizeof (dh_params) - 1,
549 val = gnutls_dh_params_import_pkcs3 (sys->dh_params, &data,
550 GNUTLS_X509_FMT_PEM);
552 gnutls_certificate_set_dh_params (sys->x509_cred,
557 msg_Err (crd, "cannot initialize DHE cipher suites: %s",
558 gnutls_strerror (val));
562 crd->open = gnutls_ServerSessionOpen;
563 crd->handshake = gnutls_ServerHandshake;
564 crd->close = gnutls_SessionClose;
570 gnutls_Deinit (VLC_OBJECT(crd));
575 * Destroys a TLS server object.
577 static void CloseServer (vlc_tls_creds_t *crd)
579 vlc_tls_creds_sys_t *sys = crd->sys;
581 /* all sessions depending on the server are now deinitialized */
582 gnutls_certificate_free_credentials (sys->x509_cred);
583 gnutls_dh_params_deinit (sys->dh_params);
586 gnutls_Deinit (VLC_OBJECT(crd));
590 #define PRIORITIES_TEXT N_("TLS cipher priorities")
591 #define PRIORITIES_LONGTEXT N_("Ciphers, key exchange methods, " \
592 "hash functions and compression methods can be selected. " \
593 "Refer to GNU TLS documentation for detailed syntax.")
594 static const char *const priorities_values[] = {
601 static const char *const priorities_text[] = {
602 N_("Performance (prioritize faster ciphers)"),
604 N_("Secure 128-bits (exclude 256-bits ciphers)"),
605 N_("Secure 256-bits (prioritize 256-bits ciphers)"),
606 N_("Export (include insecure ciphers)"),
610 set_shortname( "GNU TLS" )
611 set_description( N_("GNU TLS transport layer security") )
612 set_capability( "tls client", 1 )
613 set_callbacks( OpenClient, CloseClient )
614 set_category( CAT_ADVANCED )
615 set_subcategory( SUBCAT_ADVANCED_NETWORK )
616 add_string ("gnutls-priorities", "NORMAL", PRIORITIES_TEXT,
617 PRIORITIES_LONGTEXT, false)
618 change_string_list (priorities_values, priorities_text)
621 set_description( N_("GNU TLS server") )
622 set_capability( "tls server", 1 )
623 set_category( CAT_ADVANCED )
624 set_subcategory( SUBCAT_ADVANCED_NETWORK )
625 set_callbacks( OpenServer, CloseServer )