]> git.sesse.net Git - vlc/blob - modules/misc/gnutls.c
gnutls: show different message if certificate is unknown or mismatching
[vlc] / modules / misc / gnutls.c
1 /*****************************************************************************
2  * gnutls.c
3  *****************************************************************************
4  * Copyright (C) 2004-2012 Rémi Denis-Courmont
5  *
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.
10  *
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.
15  *
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  *****************************************************************************/
20
21 /*****************************************************************************
22  * Preamble
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <time.h>
30 #include <errno.h>
31 #include <assert.h>
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_tls.h>
36 #include <vlc_block.h>
37 #include <vlc_dialog.h>
38
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)
44 #endif
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)
50 #endif
51 #include "dhparams.h"
52
53 /*****************************************************************************
54  * Module descriptor
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 *);
60
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[] = {
66     "PERFORMANCE",
67     "NORMAL",
68     "SECURE128",
69     "SECURE256",
70     "EXPORT",
71 };
72 static const char *const priorities_text[] = {
73     N_("Performance (prioritize faster ciphers)"),
74     N_("Normal"),
75     N_("Secure 128-bits (exclude 256-bits ciphers)"),
76     N_("Secure 256-bits (prioritize 256-bits ciphers)"),
77     N_("Export (include insecure ciphers)"),
78 };
79
80 vlc_module_begin ()
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_MISC )
87
88     add_submodule ()
89         set_description( N_("GNU TLS server") )
90         set_capability( "tls server", 1 )
91         set_category( CAT_ADVANCED )
92         set_subcategory( SUBCAT_ADVANCED_MISC )
93         set_callbacks( OpenServer, CloseServer )
94
95         add_string ("gnutls-priorities", "NORMAL", PRIORITIES_TEXT,
96                     PRIORITIES_LONGTEXT, false)
97             change_string_list (priorities_values, priorities_text)
98 vlc_module_end ()
99
100 static vlc_mutex_t gnutls_mutex = VLC_STATIC_MUTEX;
101
102 /**
103  * Initializes GnuTLS with proper locking.
104  * @return VLC_SUCCESS on success, a VLC error code otherwise.
105  */
106 static int gnutls_Init (vlc_object_t *p_this)
107 {
108     int ret = VLC_EGENERIC;
109
110     vlc_mutex_lock (&gnutls_mutex);
111     if (gnutls_global_init ())
112     {
113         msg_Err (p_this, "cannot initialize GnuTLS");
114         goto error;
115     }
116
117     const char *psz_version = gnutls_check_version ("2.6.6");
118     if (psz_version == NULL)
119     {
120         msg_Err (p_this, "unsupported GnuTLS version");
121         gnutls_global_deinit ();
122         goto error;
123     }
124
125     msg_Dbg (p_this, "GnuTLS v%s initialized", psz_version);
126     ret = VLC_SUCCESS;
127
128 error:
129     vlc_mutex_unlock (&gnutls_mutex);
130     return ret;
131 }
132
133
134 /**
135  * Deinitializes GnuTLS.
136  */
137 static void gnutls_Deinit (vlc_object_t *p_this)
138 {
139     vlc_mutex_lock (&gnutls_mutex);
140
141     gnutls_global_deinit ();
142     msg_Dbg (p_this, "GnuTLS deinitialized");
143     vlc_mutex_unlock (&gnutls_mutex);
144 }
145
146
147 static int gnutls_Error (vlc_object_t *obj, int val)
148 {
149     switch (val)
150     {
151         case GNUTLS_E_AGAIN:
152 #ifdef WIN32
153             WSASetLastError (WSAEWOULDBLOCK);
154 #else
155             errno = EAGAIN;
156 #endif
157             break;
158
159         case GNUTLS_E_INTERRUPTED:
160 #ifdef WIN32
161             WSASetLastError (WSAEINTR);
162 #else
163             errno = EINTR;
164 #endif
165             break;
166
167         default:
168             msg_Err (obj, "%s", gnutls_strerror (val));
169 #ifndef NDEBUG
170             if (!gnutls_error_is_fatal (val))
171                 msg_Err (obj, "Error above should be handled");
172 #endif
173 #ifdef WIN32
174             WSASetLastError (WSAECONNRESET);
175 #else
176             errno = ECONNRESET;
177 #endif
178     }
179     return -1;
180 }
181 #define gnutls_Error(o, val) gnutls_Error(VLC_OBJECT(o), val)
182
183 struct vlc_tls_sys
184 {
185     gnutls_session_t session;
186     bool handshaked;
187 };
188
189
190 /**
191  * Sends data through a TLS session.
192  */
193 static int gnutls_Send (void *opaque, const void *buf, size_t length)
194 {
195     vlc_tls_t *session = opaque;
196     vlc_tls_sys_t *sys = session->sys;
197
198     int val = gnutls_record_send (sys->session, buf, length);
199     return (val < 0) ? gnutls_Error (session, val) : val;
200 }
201
202
203 /**
204  * Receives data through a TLS session.
205  */
206 static int gnutls_Recv (void *opaque, void *buf, size_t length)
207 {
208     vlc_tls_t *session = opaque;
209     vlc_tls_sys_t *sys = session->sys;
210
211     int val = gnutls_record_recv (sys->session, buf, length);
212     return (val < 0) ? gnutls_Error (session, val) : val;
213 }
214
215
216 /**
217  * Starts or continues the TLS handshake.
218  *
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.
222  */
223 static int gnutls_ContinueHandshake (vlc_tls_t *session, const char *host,
224                                      const char *service)
225 {
226     vlc_tls_sys_t *sys = session->sys;
227     int val;
228
229 #ifdef WIN32
230     WSASetLastError (0);
231 #endif
232     val = gnutls_handshake (sys->session);
233     if ((val == GNUTLS_E_AGAIN) || (val == GNUTLS_E_INTERRUPTED))
234         return 1 + gnutls_record_get_direction (sys->session);
235
236     if (val < 0)
237     {
238 #ifdef WIN32
239         msg_Dbg (session, "Winsock error %d", WSAGetLastError ());
240 #endif
241         msg_Err (session, "TLS handshake error: %s", gnutls_strerror (val));
242         return -1;
243     }
244
245     sys->handshaked = true;
246     (void) host; (void) service;
247     return 0;
248 }
249
250
251 /**
252  * Looks up certificate in known hosts data base.
253  * @return 0 on success, -1 on failure.
254  */
255 static int gnutls_CertSearch (vlc_tls_t *obj, const char *host,
256                               const char *service,
257                               const gnutls_datum_t *restrict datum)
258 {
259     assert (host != NULL);
260
261     /* Look up mismatching certificate in store */
262     int val = gnutls_verify_stored_pubkey (NULL, NULL, host, service,
263                                            GNUTLS_CRT_X509, datum, 0);
264     const char *msg;
265     switch (val)
266     {
267         case 0:
268             msg_Dbg (obj, "certificate key match for %s", host);
269             return 0;
270         case GNUTLS_E_NO_CERTIFICATE_FOUND:
271             msg_Dbg (obj, "no known certificates for %s", host);
272             msg = N_("You attempted to reach %s. "
273                 "However the security certificate presented by the server "
274                 "is unknown and could not be authenticated by any trusted "
275                 "Certfication Authority. "
276                 "This problem may be caused by a configuration error "
277                 "or an attempt to breach your security or your privacy.\n\n"
278                 "If in doubt, abort now.\n");
279             break;
280         case GNUTLS_E_CERTIFICATE_KEY_MISMATCH:
281             msg_Dbg (obj, "certificate keys mismatch for %s", host);
282             msg = N_("You attempted to reach %s. "
283                 "However the security certificate presented by the server "
284                 "changed since the previous visit "
285                 "and was not authentication by any trusted "
286                 "Certfication Authority. "
287                 "This problem may be caused by a configuration error "
288                 "or an attempt to breach your security or your privacy.\n\n"
289                 "If in doubt, abort now.\n");
290             break;
291         default:
292             msg_Err (obj, "certificate key match error for %s: %s", host,
293                      gnutls_strerror (val));
294             return -1;
295     }
296
297     if (dialog_Question (obj, _("Insecure site"), vlc_gettext (msg),
298                          _("Abort"), _("View certificate"), NULL, host) != 2)
299         return -1;
300
301     gnutls_x509_crt_t cert;
302     gnutls_datum_t desc;
303
304     if (gnutls_x509_crt_init (&cert))
305         return -1;
306     if (gnutls_x509_crt_import (cert, datum, GNUTLS_X509_FMT_DER)
307      || gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE, &desc))
308     {
309         gnutls_x509_crt_deinit (cert);
310         return -1;
311     }
312     gnutls_x509_crt_deinit (cert);
313
314     val = dialog_Question (obj, _("Insecure site"),
315          _("This is the certificate presented by %s:\n%s\n\n"
316            "If in doubt, abort now.\n"),
317                            _("Abort"), _("Accept 24 hours"),
318                            _("Accept permanently"), host, desc.data);
319     gnutls_free (desc.data);
320
321     time_t expiry = 0;
322     switch (val)
323     {
324         case 2:
325             time (&expiry);
326             expiry += 24 * 60 * 60;
327         case 3:
328             val = gnutls_store_pubkey (NULL, NULL, host, service,
329                                        GNUTLS_CRT_X509, datum, expiry, 0);
330             if (val)
331                 msg_Err (obj, "cannot store X.509 certificate: %s",
332                          gnutls_strerror (val));
333             return 0;
334     }
335     return -1;
336 }
337
338
339 static struct
340 {
341     int flag;
342     const char msg[43];
343     bool strict;
344 } cert_errs[] =
345 {
346     { GNUTLS_CERT_INVALID,
347         "Certificate could not be verified", false },
348     { GNUTLS_CERT_REVOKED,
349         "Certificate was revoked", true },
350     { GNUTLS_CERT_SIGNER_NOT_FOUND,
351         "Certificate's signer was not found", false },
352     { GNUTLS_CERT_SIGNER_NOT_CA,
353         "Certificate's signer is not a CA", true },
354     { GNUTLS_CERT_INSECURE_ALGORITHM,
355       "Insecure certificate signature algorithm", true },
356     { GNUTLS_CERT_NOT_ACTIVATED,
357         "Certificate is not yet activated", true },
358     { GNUTLS_CERT_EXPIRED,
359         "Certificate has expired", true },
360 };
361
362
363 static int gnutls_HandshakeAndValidate (vlc_tls_t *session, const char *host,
364                                         const char *service)
365 {
366     vlc_tls_sys_t *sys = session->sys;
367
368     int val = gnutls_ContinueHandshake (session, host, service);
369     if (val)
370         return val;
371
372     /* certificates chain verification */
373     unsigned status;
374
375     val = gnutls_certificate_verify_peers2 (sys->session, &status);
376     if (val)
377     {
378         msg_Err (session, "Certificate verification error: %s",
379                  gnutls_strerror (val));
380         return -1;
381     }
382
383     if (status)
384     {
385         msg_Err (session, "Certificate verification failure:");
386         for (size_t i = 0; i < sizeof (cert_errs) / sizeof (cert_errs[0]); i++)
387             if (status & cert_errs[i].flag)
388             {
389                 msg_Err (session, " * %s", cert_errs[i].msg);
390                 status &= ~cert_errs[i].flag;
391                 if (cert_errs[i].strict)
392                     val = -1;
393             }
394
395         if (status)
396         {
397             msg_Err (session, " * Unknown verification error 0x%04X", status);
398             val = -1;
399         }
400         status = -1;
401     }
402
403     /* certificate (host)name verification */
404     const gnutls_datum_t *data;
405     unsigned count;
406     data = gnutls_certificate_get_peers (sys->session, &count);
407     if (data == NULL || count == 0)
408     {
409         msg_Err (session, "Peer certificate not available");
410         return -1;
411     }
412     msg_Dbg (session, "%u certificate(s) in the list", count);
413
414     if (val || host == NULL)
415         return val;
416     if (status && gnutls_CertSearch (session, host, service, data))
417         return -1;
418
419     gnutls_x509_crt_t cert;
420     val = gnutls_x509_crt_init (&cert);
421     if (val)
422     {
423         msg_Err (session, "X.509 fatal error: %s", gnutls_strerror (val));
424         return -1;
425     }
426
427     val = gnutls_x509_crt_import (cert, data, GNUTLS_X509_FMT_DER);
428     if (val)
429     {
430         msg_Err (session, "Certificate import error: %s",
431                  gnutls_strerror (val));
432         goto error;
433     }
434
435     val = !gnutls_x509_crt_check_hostname (cert, host);
436     if (val)
437     {
438         msg_Err (session, "Certificate does not match \"%s\"", host);
439         val = gnutls_CertSearch (session, host, service, data);
440     }
441 error:
442     gnutls_x509_crt_init (&cert);
443     return val ? -1 : 0;
444 }
445
446 static int
447 gnutls_SessionPrioritize (vlc_object_t *obj, gnutls_session_t session)
448 {
449     char *priorities = var_InheritString (obj, "gnutls-priorities");
450     if (unlikely(priorities == NULL))
451         return VLC_ENOMEM;
452
453     const char *errp;
454     int val = gnutls_priority_set_direct (session, priorities, &errp);
455     if (val < 0)
456     {
457         msg_Err (obj, "cannot set TLS priorities \"%s\": %s", errp,
458                  gnutls_strerror (val));
459         val = VLC_EGENERIC;
460     }
461     else
462         val = VLC_SUCCESS;
463     free (priorities);
464     return val;
465 }
466
467
468 /**
469  * TLS credentials private data
470  */
471 struct vlc_tls_creds_sys
472 {
473     gnutls_certificate_credentials_t x509_cred;
474     gnutls_dh_params_t dh_params; /* XXX: used for server only */
475     int (*handshake) (vlc_tls_t *, const char *, const char *);
476         /* ^^ XXX: useful for server only */
477 };
478
479
480 /**
481  * Terminates TLS session and releases session data.
482  * You still have to close the socket yourself.
483  */
484 static void gnutls_SessionClose (vlc_tls_creds_t *crd, vlc_tls_t *session)
485 {
486     vlc_tls_sys_t *sys = session->sys;
487
488     if (sys->handshaked)
489         gnutls_bye (sys->session, GNUTLS_SHUT_WR);
490     gnutls_deinit (sys->session);
491
492     free (sys);
493     (void) crd;
494 }
495
496
497 /**
498  * Initializes a server-side TLS session.
499  */
500 static int gnutls_SessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
501                                int type, int fd)
502 {
503     vlc_tls_sys_t *sys = malloc (sizeof (*session->sys));
504     if (unlikely(sys == NULL))
505         return VLC_ENOMEM;
506
507     session->sys = sys;
508     session->sock.p_sys = session;
509     session->sock.pf_send = gnutls_Send;
510     session->sock.pf_recv = gnutls_Recv;
511     session->handshake = crd->sys->handshake;
512     sys->handshaked = false;
513
514     int val = gnutls_init (&sys->session, type);
515     if (val != 0)
516     {
517         msg_Err (session, "cannot initialize TLS session: %s",
518                  gnutls_strerror (val));
519         free (sys);
520         return VLC_EGENERIC;
521     }
522
523     if (gnutls_SessionPrioritize (VLC_OBJECT (crd), sys->session))
524         goto error;
525
526     val = gnutls_credentials_set (sys->session, GNUTLS_CRD_CERTIFICATE,
527                                   crd->sys->x509_cred);
528     if (val < 0)
529     {
530         msg_Err (session, "cannot set TLS session credentials: %s",
531                  gnutls_strerror (val));
532         goto error;
533     }
534
535     gnutls_transport_set_ptr (sys->session,
536                               (gnutls_transport_ptr_t)(intptr_t)fd);
537     return VLC_SUCCESS;
538
539 error:
540     gnutls_SessionClose (crd, session);
541     return VLC_EGENERIC;
542 }
543
544 static int gnutls_ServerSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
545                                      int fd, const char *hostname)
546 {
547     int val = gnutls_SessionOpen (crd, session, GNUTLS_SERVER, fd);
548     if (val != VLC_SUCCESS)
549         return val;
550
551     if (session->handshake == gnutls_HandshakeAndValidate)
552         gnutls_certificate_server_set_request (session->sys->session,
553                                                GNUTLS_CERT_REQUIRE);
554     assert (hostname == NULL);
555     return VLC_SUCCESS;
556 }
557
558 static int gnutls_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
559                                      int fd, const char *hostname)
560 {
561     int val = gnutls_SessionOpen (crd, session, GNUTLS_CLIENT, fd);
562     if (val != VLC_SUCCESS)
563         return val;
564
565     vlc_tls_sys_t *sys = session->sys;
566
567     /* minimum DH prime bits */
568     gnutls_dh_set_prime_bits (sys->session, 1024);
569
570     if (likely(hostname != NULL))
571         /* fill Server Name Indication */
572         gnutls_server_name_set (sys->session, GNUTLS_NAME_DNS,
573                                 hostname, strlen (hostname));
574
575     return VLC_SUCCESS;
576 }
577
578
579 /**
580  * Adds one or more Certificate Authorities to the trusted set.
581  *
582  * @param path (UTF-8) path to an X.509 certificates list.
583  *
584  * @return -1 on error, 0 on success.
585  */
586 static int gnutls_AddCA (vlc_tls_creds_t *crd, const char *path)
587 {
588     block_t *block = block_FilePath (path);
589     if (block == NULL)
590     {
591         msg_Err (crd, "cannot read trusted CA from %s: %m", path);
592         return VLC_EGENERIC;
593     }
594
595     gnutls_datum_t d = {
596        .data = block->p_buffer,
597        .size = block->i_buffer,
598     };
599
600     int val = gnutls_certificate_set_x509_trust_mem (crd->sys->x509_cred, &d,
601                                                      GNUTLS_X509_FMT_PEM);
602     block_Release (block);
603     if (val < 0)
604     {
605         msg_Err (crd, "cannot load trusted CA from %s: %s", path,
606                  gnutls_strerror (val));
607         return VLC_EGENERIC;
608     }
609     msg_Dbg (crd, " %d trusted CA%s added from %s", val, (val != 1) ? "s" : "",
610              path);
611
612     /* enables peer's certificate verification */
613     crd->sys->handshake = gnutls_HandshakeAndValidate;
614     return VLC_SUCCESS;
615 }
616
617
618 /**
619  * Adds a Certificates Revocation List to be sent to TLS clients.
620  *
621  * @param path (UTF-8) path of the CRL file.
622  *
623  * @return -1 on error, 0 on success.
624  */
625 static int gnutls_AddCRL (vlc_tls_creds_t *crd, const char *path)
626 {
627     block_t *block = block_FilePath (path);
628     if (block == NULL)
629     {
630         msg_Err (crd, "cannot read CRL from %s: %m", path);
631         return VLC_EGENERIC;
632     }
633
634     gnutls_datum_t d = {
635        .data = block->p_buffer,
636        .size = block->i_buffer,
637     };
638
639     int val = gnutls_certificate_set_x509_crl_mem (crd->sys->x509_cred, &d,
640                                                    GNUTLS_X509_FMT_PEM);
641     block_Release (block);
642     if (val < 0)
643     {
644         msg_Err (crd, "cannot add CRL (%s): %s", path, gnutls_strerror (val));
645         return VLC_EGENERIC;
646     }
647     msg_Dbg (crd, "%d CRL%s added from %s", val, (val != 1) ? "s" : "", path);
648     return VLC_SUCCESS;
649 }
650
651
652 /**
653  * Allocates a whole server's TLS credentials.
654  */
655 static int OpenServer (vlc_tls_creds_t *crd, const char *cert, const char *key)
656 {
657     int val;
658
659     if (gnutls_Init (VLC_OBJECT(crd)))
660         return VLC_EGENERIC;
661
662     vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
663     if (unlikely(sys == NULL))
664         goto error;
665
666     crd->sys     = sys;
667     crd->add_CA  = gnutls_AddCA;
668     crd->add_CRL = gnutls_AddCRL;
669     crd->open    = gnutls_ServerSessionOpen;
670     crd->close   = gnutls_SessionClose;
671     /* No certificate validation by default */
672     sys->handshake  = gnutls_ContinueHandshake;
673
674     /* Sets server's credentials */
675     val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
676     if (val != 0)
677     {
678         msg_Err (crd, "cannot allocate credentials: %s",
679                  gnutls_strerror (val));
680         goto error;
681     }
682
683     block_t *certblock = block_FilePath (cert);
684     if (certblock == NULL)
685     {
686         msg_Err (crd, "cannot read certificate chain from %s: %m", cert);
687         return VLC_EGENERIC;
688     }
689
690     block_t *keyblock = block_FilePath (key);
691     if (keyblock == NULL)
692     {
693         msg_Err (crd, "cannot read private key from %s: %m", key);
694         block_Release (certblock);
695         return VLC_EGENERIC;
696     }
697
698     gnutls_datum_t pub = {
699        .data = certblock->p_buffer,
700        .size = certblock->i_buffer,
701     }, priv = {
702        .data = keyblock->p_buffer,
703        .size = keyblock->i_buffer,
704     };
705
706     val = gnutls_certificate_set_x509_key_mem (sys->x509_cred, &pub, &priv,
707                                                 GNUTLS_X509_FMT_PEM);
708     block_Release (keyblock);
709     block_Release (certblock);
710     if (val < 0)
711     {
712         msg_Err (crd, "cannot load X.509 key: %s", gnutls_strerror (val));
713         gnutls_certificate_free_credentials (sys->x509_cred);
714         goto error;
715     }
716
717     /* FIXME:
718      * - support other cipher suites
719      */
720     val = gnutls_dh_params_init (&sys->dh_params);
721     if (val >= 0)
722     {
723         const gnutls_datum_t data = {
724             .data = (unsigned char *)dh_params,
725             .size = sizeof (dh_params) - 1,
726         };
727
728         val = gnutls_dh_params_import_pkcs3 (sys->dh_params, &data,
729                                              GNUTLS_X509_FMT_PEM);
730         if (val == 0)
731             gnutls_certificate_set_dh_params (sys->x509_cred,
732                                               sys->dh_params);
733     }
734     if (val < 0)
735     {
736         msg_Err (crd, "cannot initialize DHE cipher suites: %s",
737                  gnutls_strerror (val));
738     }
739
740     return VLC_SUCCESS;
741
742 error:
743     free (sys);
744     gnutls_Deinit (VLC_OBJECT(crd));
745     return VLC_EGENERIC;
746 }
747
748 /**
749  * Destroys a TLS server object.
750  */
751 static void CloseServer (vlc_tls_creds_t *crd)
752 {
753     vlc_tls_creds_sys_t *sys = crd->sys;
754
755     /* all sessions depending on the server are now deinitialized */
756     gnutls_certificate_free_credentials (sys->x509_cred);
757     gnutls_dh_params_deinit (sys->dh_params);
758     free (sys);
759
760     gnutls_Deinit (VLC_OBJECT(crd));
761 }
762
763 /**
764  * Initializes a client-side TLS credentials.
765  */
766 static int OpenClient (vlc_tls_creds_t *crd)
767 {
768     if (gnutls_Init (VLC_OBJECT(crd)))
769         return VLC_EGENERIC;
770
771     vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
772     if (unlikely(sys == NULL))
773         goto error;
774
775     crd->sys = sys;
776     //crd->add_CA = gnutls_AddCA;
777     //crd->add_CRL = gnutls_AddCRL;
778     crd->open = gnutls_ClientSessionOpen;
779     crd->close = gnutls_SessionClose;
780     sys->handshake = gnutls_HandshakeAndValidate;
781
782     int val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
783     if (val != 0)
784     {
785         msg_Err (crd, "cannot allocate credentials: %s",
786                  gnutls_strerror (val));
787         goto error;
788     }
789
790     val = gnutls_certificate_set_x509_system_trust (sys->x509_cred);
791     if (val < 0)
792         msg_Err (crd, "cannot load trusted Certificate Authorities: %s",
793                  gnutls_strerror (val));
794     else
795         msg_Dbg (crd, "loaded %d trusted CAs", val);
796
797     gnutls_certificate_set_verify_flags (sys->x509_cred,
798                                          GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
799
800     return VLC_SUCCESS;
801 error:
802     free (sys);
803     gnutls_Deinit (VLC_OBJECT(crd));
804     return VLC_EGENERIC;
805 }
806
807 static void CloseClient (vlc_tls_creds_t *crd)
808 {
809     vlc_tls_creds_sys_t *sys = crd->sys;
810
811     gnutls_certificate_free_credentials (sys->x509_cred);
812     free (sys);
813
814     gnutls_Deinit (VLC_OBJECT(crd));
815 }