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 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
36 #include <vlc_block.h>
37 #include <vlc_dialog.h>
39 #include <gnutls/gnutls.h>
40 #include <gnutls/x509.h>
41 #if (GNUTLS_VERSION_NUMBER < 0x030014)
42 # define gnutls_certificate_set_x509_system_trust(c) \
43 (c, GNUTLS_E_UNIMPLEMENTED_FEATURE)
45 #if (GNUTLS_VERSION_NUMBER < 0x03000D)
46 # define gnutls_verify_stored_pubkey(db,tdb,host,serv,ctype,cert,fl) \
47 (db, host, serv, ctype, cert, fl, GNUTLS_E_NO_CERTIFICATE_FOUND)
48 # define gnutls_store_pubkey(db,tdb,host,serv,ctype,cert,e,fl) \
49 (db, host, serv, ctype, cert, fl, GNUTLS_E_UNIMPLEMENTED_FEATURE)
53 /*****************************************************************************
55 *****************************************************************************/
56 static int OpenClient (vlc_tls_creds_t
*);
57 static void CloseClient (vlc_tls_creds_t
*);
58 static int OpenServer (vlc_tls_creds_t
*, const char *, const char *);
59 static void CloseServer (vlc_tls_creds_t
*);
61 #define PRIORITIES_TEXT N_("TLS cipher priorities")
62 #define PRIORITIES_LONGTEXT N_("Ciphers, key exchange methods, " \
63 "hash functions and compression methods can be selected. " \
64 "Refer to GNU TLS documentation for detailed syntax.")
65 static const char *const priorities_values
[] = {
72 static const char *const priorities_text
[] = {
73 N_("Performance (prioritize faster ciphers)"),
75 N_("Secure 128-bits (exclude 256-bits ciphers)"),
76 N_("Secure 256-bits (prioritize 256-bits ciphers)"),
77 N_("Export (include insecure ciphers)"),
81 set_shortname( "GNU TLS" )
82 set_description( N_("GNU TLS transport layer security") )
83 set_capability( "tls client", 1 )
84 set_callbacks( OpenClient
, CloseClient
)
85 set_category( CAT_ADVANCED
)
86 set_subcategory( SUBCAT_ADVANCED_NETWORK
)
89 set_description( N_("GNU TLS server") )
90 set_capability( "tls server", 1 )
91 set_category( CAT_ADVANCED
)
92 set_subcategory( SUBCAT_ADVANCED_NETWORK
)
93 set_callbacks( OpenServer
, CloseServer
)
95 add_string ("gnutls-priorities", "NORMAL", PRIORITIES_TEXT
,
96 PRIORITIES_LONGTEXT
, false)
97 change_string_list (priorities_values
, priorities_text
)
100 static vlc_mutex_t gnutls_mutex
= VLC_STATIC_MUTEX
;
103 * Initializes GnuTLS with proper locking.
104 * @return VLC_SUCCESS on success, a VLC error code otherwise.
106 static int gnutls_Init (vlc_object_t
*p_this
)
108 int ret
= VLC_EGENERIC
;
110 vlc_mutex_lock (&gnutls_mutex
);
111 if (gnutls_global_init ())
113 msg_Err (p_this
, "cannot initialize GnuTLS");
117 const char *psz_version
= gnutls_check_version ("2.6.6");
118 if (psz_version
== NULL
)
120 msg_Err (p_this
, "unsupported GnuTLS version");
121 gnutls_global_deinit ();
125 msg_Dbg (p_this
, "GnuTLS v%s initialized", psz_version
);
129 vlc_mutex_unlock (&gnutls_mutex
);
135 * Deinitializes GnuTLS.
137 static void gnutls_Deinit (vlc_object_t
*p_this
)
139 vlc_mutex_lock (&gnutls_mutex
);
141 gnutls_global_deinit ();
142 msg_Dbg (p_this
, "GnuTLS deinitialized");
143 vlc_mutex_unlock (&gnutls_mutex
);
147 static int gnutls_Error (vlc_object_t
*obj
, int val
)
153 WSASetLastError (WSAEWOULDBLOCK
);
159 case GNUTLS_E_INTERRUPTED
:
161 WSASetLastError (WSAEINTR
);
168 msg_Err (obj
, "%s", gnutls_strerror (val
));
170 if (!gnutls_error_is_fatal (val
))
171 msg_Err (obj
, "Error above should be handled");
174 WSASetLastError (WSAECONNRESET
);
181 #define gnutls_Error(o, val) gnutls_Error(VLC_OBJECT(o), val)
185 gnutls_session_t session
;
191 * Sends data through a TLS session.
193 static int gnutls_Send (void *opaque
, const void *buf
, size_t length
)
195 vlc_tls_t
*session
= opaque
;
196 vlc_tls_sys_t
*sys
= session
->sys
;
198 int val
= gnutls_record_send (sys
->session
, buf
, length
);
199 return (val
< 0) ? gnutls_Error (session
, val
) : val
;
204 * Receives data through a TLS session.
206 static int gnutls_Recv (void *opaque
, void *buf
, size_t length
)
208 vlc_tls_t
*session
= opaque
;
209 vlc_tls_sys_t
*sys
= session
->sys
;
211 int val
= gnutls_record_recv (sys
->session
, buf
, length
);
212 return (val
< 0) ? gnutls_Error (session
, val
) : val
;
217 * Starts or continues the TLS handshake.
219 * @return -1 on fatal error, 0 on successful handshake completion,
220 * 1 if more would-be blocking recv is needed,
221 * 2 if more would-be blocking send is required.
223 static int gnutls_ContinueHandshake (vlc_tls_t
*session
, const char *host
,
226 vlc_tls_sys_t
*sys
= session
->sys
;
234 val
= gnutls_handshake (sys
->session
);
235 msg_Dbg (session
, "TLS handshake: %s", gnutls_strerror (val
));
237 if ((val
== GNUTLS_E_AGAIN
) || (val
== GNUTLS_E_INTERRUPTED
))
238 /* I/O event: return to caller's poll() loop */
239 return 1 + gnutls_record_get_direction (sys
->session
);
241 while (val
< 0 && !gnutls_error_is_fatal (val
));
246 msg_Dbg (session
, "Winsock error %d", WSAGetLastError ());
248 msg_Err (session
, "TLS handshake error: %s", gnutls_strerror (val
));
252 sys
->handshaked
= true;
253 (void) host
; (void) service
;
259 * Looks up certificate in known hosts data base.
260 * @return 0 on success, -1 on failure.
262 static int gnutls_CertSearch (vlc_tls_t
*obj
, const char *host
,
264 const gnutls_datum_t
*restrict datum
)
266 assert (host
!= NULL
);
268 /* Look up mismatching certificate in store */
269 int val
= gnutls_verify_stored_pubkey (NULL
, NULL
, host
, service
,
270 GNUTLS_CRT_X509
, datum
, 0);
275 msg_Dbg (obj
, "certificate key match for %s", host
);
277 case GNUTLS_E_NO_CERTIFICATE_FOUND
:
278 msg_Dbg (obj
, "no known certificates for %s", host
);
279 msg
= N_("You attempted to reach %s. "
280 "However the security certificate presented by the server "
281 "is unknown and could not be authenticated by any trusted "
282 "Certification Authority. "
283 "This problem may be caused by a configuration error "
284 "or an attempt to breach your security or your privacy.\n\n"
285 "If in doubt, abort now.\n");
287 case GNUTLS_E_CERTIFICATE_KEY_MISMATCH
:
288 msg_Dbg (obj
, "certificate keys mismatch for %s", host
);
289 msg
= N_("You attempted to reach %s. "
290 "However the security certificate presented by the server "
291 "changed since the previous visit "
292 "and was not authenticated by any trusted "
293 "Certification Authority. "
294 "This problem may be caused by a configuration error "
295 "or an attempt to breach your security or your privacy.\n\n"
296 "If in doubt, abort now.\n");
299 msg_Err (obj
, "certificate key match error for %s: %s", host
,
300 gnutls_strerror (val
));
304 if (dialog_Question (obj
, _("Insecure site"), vlc_gettext (msg
),
305 _("Abort"), _("View certificate"), NULL
, host
) != 2)
308 gnutls_x509_crt_t cert
;
311 if (gnutls_x509_crt_init (&cert
))
313 if (gnutls_x509_crt_import (cert
, datum
, GNUTLS_X509_FMT_DER
)
314 || gnutls_x509_crt_print (cert
, GNUTLS_CRT_PRINT_ONELINE
, &desc
))
316 gnutls_x509_crt_deinit (cert
);
319 gnutls_x509_crt_deinit (cert
);
321 val
= dialog_Question (obj
, _("Insecure site"),
322 _("This is the certificate presented by %s:\n%s\n\n"
323 "If in doubt, abort now.\n"),
324 _("Abort"), _("Accept 24 hours"),
325 _("Accept permanently"), host
, desc
.data
);
326 gnutls_free (desc
.data
);
333 expiry
+= 24 * 60 * 60;
335 val
= gnutls_store_pubkey (NULL
, NULL
, host
, service
,
336 GNUTLS_CRT_X509
, datum
, expiry
, 0);
338 msg_Err (obj
, "cannot store X.509 certificate: %s",
339 gnutls_strerror (val
));
353 { GNUTLS_CERT_INVALID
,
354 "Certificate could not be verified", false },
355 { GNUTLS_CERT_REVOKED
,
356 "Certificate was revoked", true },
357 { GNUTLS_CERT_SIGNER_NOT_FOUND
,
358 "Certificate's signer was not found", false },
359 { GNUTLS_CERT_SIGNER_NOT_CA
,
360 "Certificate's signer is not a CA", true },
361 { GNUTLS_CERT_INSECURE_ALGORITHM
,
362 "Insecure certificate signature algorithm", true },
363 { GNUTLS_CERT_NOT_ACTIVATED
,
364 "Certificate is not yet activated", true },
365 { GNUTLS_CERT_EXPIRED
,
366 "Certificate has expired", true },
370 static int gnutls_HandshakeAndValidate (vlc_tls_t
*session
, const char *host
,
373 vlc_tls_sys_t
*sys
= session
->sys
;
375 int val
= gnutls_ContinueHandshake (session
, host
, service
);
379 /* certificates chain verification */
382 val
= gnutls_certificate_verify_peers2 (sys
->session
, &status
);
385 msg_Err (session
, "Certificate verification error: %s",
386 gnutls_strerror (val
));
392 msg_Err (session
, "Certificate verification failure:");
393 for (size_t i
= 0; i
< sizeof (cert_errs
) / sizeof (cert_errs
[0]); i
++)
394 if (status
& cert_errs
[i
].flag
)
396 msg_Err (session
, " * %s", cert_errs
[i
].msg
);
397 status
&= ~cert_errs
[i
].flag
;
398 if (cert_errs
[i
].strict
)
404 msg_Err (session
, " * Unknown verification error 0x%04X", status
);
410 /* certificate (host)name verification */
411 const gnutls_datum_t
*data
;
413 data
= gnutls_certificate_get_peers (sys
->session
, &count
);
414 if (data
== NULL
|| count
== 0)
416 msg_Err (session
, "Peer certificate not available");
419 msg_Dbg (session
, "%u certificate(s) in the list", count
);
421 if (val
|| host
== NULL
)
423 if (status
&& gnutls_CertSearch (session
, host
, service
, data
))
426 gnutls_x509_crt_t cert
;
427 val
= gnutls_x509_crt_init (&cert
);
430 msg_Err (session
, "X.509 fatal error: %s", gnutls_strerror (val
));
434 val
= gnutls_x509_crt_import (cert
, data
, GNUTLS_X509_FMT_DER
);
437 msg_Err (session
, "Certificate import error: %s",
438 gnutls_strerror (val
));
442 val
= !gnutls_x509_crt_check_hostname (cert
, host
);
445 msg_Err (session
, "Certificate does not match \"%s\"", host
);
446 val
= gnutls_CertSearch (session
, host
, service
, data
);
449 gnutls_x509_crt_init (&cert
);
454 gnutls_SessionPrioritize (vlc_object_t
*obj
, gnutls_session_t session
)
456 char *priorities
= var_InheritString (obj
, "gnutls-priorities");
457 if (unlikely(priorities
== NULL
))
461 int val
= gnutls_priority_set_direct (session
, priorities
, &errp
);
464 msg_Err (obj
, "cannot set TLS priorities \"%s\": %s", errp
,
465 gnutls_strerror (val
));
476 * TLS credentials private data
478 struct vlc_tls_creds_sys
480 gnutls_certificate_credentials_t x509_cred
;
481 gnutls_dh_params_t dh_params
; /* XXX: used for server only */
482 int (*handshake
) (vlc_tls_t
*, const char *, const char *);
483 /* ^^ XXX: useful for server only */
488 * Terminates TLS session and releases session data.
489 * You still have to close the socket yourself.
491 static void gnutls_SessionClose (vlc_tls_creds_t
*crd
, vlc_tls_t
*session
)
493 vlc_tls_sys_t
*sys
= session
->sys
;
496 gnutls_bye (sys
->session
, GNUTLS_SHUT_WR
);
497 gnutls_deinit (sys
->session
);
505 * Initializes a server-side TLS session.
507 static int gnutls_SessionOpen (vlc_tls_creds_t
*crd
, vlc_tls_t
*session
,
510 vlc_tls_sys_t
*sys
= malloc (sizeof (*session
->sys
));
511 if (unlikely(sys
== NULL
))
515 session
->sock
.p_sys
= session
;
516 session
->sock
.pf_send
= gnutls_Send
;
517 session
->sock
.pf_recv
= gnutls_Recv
;
518 session
->handshake
= crd
->sys
->handshake
;
519 sys
->handshaked
= false;
521 int val
= gnutls_init (&sys
->session
, type
);
524 msg_Err (session
, "cannot initialize TLS session: %s",
525 gnutls_strerror (val
));
530 if (gnutls_SessionPrioritize (VLC_OBJECT (crd
), sys
->session
))
533 val
= gnutls_credentials_set (sys
->session
, GNUTLS_CRD_CERTIFICATE
,
534 crd
->sys
->x509_cred
);
537 msg_Err (session
, "cannot set TLS session credentials: %s",
538 gnutls_strerror (val
));
542 gnutls_transport_set_ptr (sys
->session
,
543 (gnutls_transport_ptr_t
)(intptr_t)fd
);
547 gnutls_SessionClose (crd
, session
);
551 static int gnutls_ServerSessionOpen (vlc_tls_creds_t
*crd
, vlc_tls_t
*session
,
552 int fd
, const char *hostname
)
554 int val
= gnutls_SessionOpen (crd
, session
, GNUTLS_SERVER
, fd
);
555 if (val
!= VLC_SUCCESS
)
558 if (session
->handshake
== gnutls_HandshakeAndValidate
)
559 gnutls_certificate_server_set_request (session
->sys
->session
,
560 GNUTLS_CERT_REQUIRE
);
561 assert (hostname
== NULL
);
565 static int gnutls_ClientSessionOpen (vlc_tls_creds_t
*crd
, vlc_tls_t
*session
,
566 int fd
, const char *hostname
)
568 int val
= gnutls_SessionOpen (crd
, session
, GNUTLS_CLIENT
, fd
);
569 if (val
!= VLC_SUCCESS
)
572 vlc_tls_sys_t
*sys
= session
->sys
;
574 /* minimum DH prime bits */
575 gnutls_dh_set_prime_bits (sys
->session
, 1024);
577 if (likely(hostname
!= NULL
))
578 /* fill Server Name Indication */
579 gnutls_server_name_set (sys
->session
, GNUTLS_NAME_DNS
,
580 hostname
, strlen (hostname
));
587 * Adds one or more Certificate Authorities to the trusted set.
589 * @param path (UTF-8) path to an X.509 certificates list.
591 * @return -1 on error, 0 on success.
593 static int gnutls_AddCA (vlc_tls_creds_t
*crd
, const char *path
)
595 block_t
*block
= block_FilePath (path
);
598 msg_Err (crd
, "cannot read trusted CA from %s: %m", path
);
603 .data
= block
->p_buffer
,
604 .size
= block
->i_buffer
,
607 int val
= gnutls_certificate_set_x509_trust_mem (crd
->sys
->x509_cred
, &d
,
608 GNUTLS_X509_FMT_PEM
);
609 block_Release (block
);
612 msg_Err (crd
, "cannot load trusted CA from %s: %s", path
,
613 gnutls_strerror (val
));
616 msg_Dbg (crd
, " %d trusted CA%s added from %s", val
, (val
!= 1) ? "s" : "",
619 /* enables peer's certificate verification */
620 crd
->sys
->handshake
= gnutls_HandshakeAndValidate
;
626 * Adds a Certificates Revocation List to be sent to TLS clients.
628 * @param path (UTF-8) path of the CRL file.
630 * @return -1 on error, 0 on success.
632 static int gnutls_AddCRL (vlc_tls_creds_t
*crd
, const char *path
)
634 block_t
*block
= block_FilePath (path
);
637 msg_Err (crd
, "cannot read CRL from %s: %m", path
);
642 .data
= block
->p_buffer
,
643 .size
= block
->i_buffer
,
646 int val
= gnutls_certificate_set_x509_crl_mem (crd
->sys
->x509_cred
, &d
,
647 GNUTLS_X509_FMT_PEM
);
648 block_Release (block
);
651 msg_Err (crd
, "cannot add CRL (%s): %s", path
, gnutls_strerror (val
));
654 msg_Dbg (crd
, "%d CRL%s added from %s", val
, (val
!= 1) ? "s" : "", path
);
660 * Allocates a whole server's TLS credentials.
662 static int OpenServer (vlc_tls_creds_t
*crd
, const char *cert
, const char *key
)
666 if (gnutls_Init (VLC_OBJECT(crd
)))
669 vlc_tls_creds_sys_t
*sys
= malloc (sizeof (*sys
));
670 if (unlikely(sys
== NULL
))
674 crd
->add_CA
= gnutls_AddCA
;
675 crd
->add_CRL
= gnutls_AddCRL
;
676 crd
->open
= gnutls_ServerSessionOpen
;
677 crd
->close
= gnutls_SessionClose
;
678 /* No certificate validation by default */
679 sys
->handshake
= gnutls_ContinueHandshake
;
681 /* Sets server's credentials */
682 val
= gnutls_certificate_allocate_credentials (&sys
->x509_cred
);
685 msg_Err (crd
, "cannot allocate credentials: %s",
686 gnutls_strerror (val
));
690 block_t
*certblock
= block_FilePath (cert
);
691 if (certblock
== NULL
)
693 msg_Err (crd
, "cannot read certificate chain from %s: %m", cert
);
697 block_t
*keyblock
= block_FilePath (key
);
698 if (keyblock
== NULL
)
700 msg_Err (crd
, "cannot read private key from %s: %m", key
);
701 block_Release (certblock
);
705 gnutls_datum_t pub
= {
706 .data
= certblock
->p_buffer
,
707 .size
= certblock
->i_buffer
,
709 .data
= keyblock
->p_buffer
,
710 .size
= keyblock
->i_buffer
,
713 val
= gnutls_certificate_set_x509_key_mem (sys
->x509_cred
, &pub
, &priv
,
714 GNUTLS_X509_FMT_PEM
);
715 block_Release (keyblock
);
716 block_Release (certblock
);
719 msg_Err (crd
, "cannot load X.509 key: %s", gnutls_strerror (val
));
720 gnutls_certificate_free_credentials (sys
->x509_cred
);
725 * - support other cipher suites
727 val
= gnutls_dh_params_init (&sys
->dh_params
);
730 const gnutls_datum_t data
= {
731 .data
= (unsigned char *)dh_params
,
732 .size
= sizeof (dh_params
) - 1,
735 val
= gnutls_dh_params_import_pkcs3 (sys
->dh_params
, &data
,
736 GNUTLS_X509_FMT_PEM
);
738 gnutls_certificate_set_dh_params (sys
->x509_cred
,
743 msg_Err (crd
, "cannot initialize DHE cipher suites: %s",
744 gnutls_strerror (val
));
751 gnutls_Deinit (VLC_OBJECT(crd
));
756 * Destroys a TLS server object.
758 static void CloseServer (vlc_tls_creds_t
*crd
)
760 vlc_tls_creds_sys_t
*sys
= crd
->sys
;
762 /* all sessions depending on the server are now deinitialized */
763 gnutls_certificate_free_credentials (sys
->x509_cred
);
764 gnutls_dh_params_deinit (sys
->dh_params
);
767 gnutls_Deinit (VLC_OBJECT(crd
));
771 * Initializes a client-side TLS credentials.
773 static int OpenClient (vlc_tls_creds_t
*crd
)
775 if (gnutls_Init (VLC_OBJECT(crd
)))
778 vlc_tls_creds_sys_t
*sys
= malloc (sizeof (*sys
));
779 if (unlikely(sys
== NULL
))
783 //crd->add_CA = gnutls_AddCA;
784 //crd->add_CRL = gnutls_AddCRL;
785 crd
->open
= gnutls_ClientSessionOpen
;
786 crd
->close
= gnutls_SessionClose
;
787 sys
->handshake
= gnutls_HandshakeAndValidate
;
789 int val
= gnutls_certificate_allocate_credentials (&sys
->x509_cred
);
792 msg_Err (crd
, "cannot allocate credentials: %s",
793 gnutls_strerror (val
));
797 val
= gnutls_certificate_set_x509_system_trust (sys
->x509_cred
);
799 msg_Err (crd
, "cannot load trusted Certificate Authorities: %s",
800 gnutls_strerror (val
));
802 msg_Dbg (crd
, "loaded %d trusted CAs", val
);
804 gnutls_certificate_set_verify_flags (sys
->x509_cred
,
805 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT
);
810 gnutls_Deinit (VLC_OBJECT(crd
));
814 static void CloseClient (vlc_tls_creds_t
*crd
)
816 vlc_tls_creds_sys_t
*sys
= crd
->sys
;
818 gnutls_certificate_free_credentials (sys
->x509_cred
);
821 gnutls_Deinit (VLC_OBJECT(crd
));