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