]> git.sesse.net Git - vlc/blob - modules/misc/gnutls.c
macosx: add new common window class in order to deduplicate code
[vlc] / modules / misc / gnutls.c
1 /*****************************************************************************
2  * gnutls.c
3  *****************************************************************************
4  * Copyright (C) 2004-2011 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 General Public License as published by
8  * the Free Software Foundation; either version 2 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 General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, 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 <errno.h>
30 #include <sys/types.h>
31 #include <errno.h>
32
33 #include <sys/stat.h>
34 #ifdef WIN32
35 # include <windows.h>
36 # include <io.h>
37 # include <wincrypt.h>
38 #else
39 # include <unistd.h>
40 #endif
41 #include <fcntl.h>
42
43 #include <vlc_common.h>
44 #include <vlc_plugin.h>
45 #include <vlc_tls.h>
46 #include <vlc_charset.h>
47 #include <vlc_fs.h>
48 #include <vlc_block.h>
49
50 #include <gcrypt.h>
51 #include <gnutls/gnutls.h>
52 #include <gnutls/x509.h>
53
54 #include <vlc_gcrypt.h>
55 #include "dhparams.h"
56
57 #include <assert.h>
58
59 /*****************************************************************************
60  * Module descriptor
61  *****************************************************************************/
62 static int  OpenClient  (vlc_tls_t *, int, const char *);
63 static void CloseClient (vlc_tls_t *);
64 static int  OpenServer  (vlc_object_t *);
65 static void CloseServer (vlc_object_t *);
66
67 #define PRIORITIES_TEXT N_("TLS cipher priorities")
68 #define PRIORITIES_LONGTEXT N_("Ciphers, key exchange methods, " \
69     "hash functions and compression methods can be selected. " \
70     "Refer to GNU TLS documentation for detailed syntax.")
71 static const char *const priorities_values[] = {
72     "PERFORMANCE",
73     "NORMAL",
74     "SECURE128",
75     "SECURE256",
76     "EXPORT",
77 };
78 static const char *const priorities_text[] = {
79     N_("Performance (prioritize faster ciphers)"),
80     N_("Normal"),
81     N_("Secure 128-bits (exclude 256-bits ciphers)"),
82     N_("Secure 256-bits (prioritize 256-bits ciphers)"),
83     N_("Export (include insecure ciphers)"),
84 };
85
86 vlc_module_begin ()
87     set_shortname( "GNU TLS" )
88     set_description( N_("GNU TLS transport layer security") )
89     set_capability( "tls client", 1 )
90     set_callbacks( OpenClient, CloseClient )
91     set_category( CAT_ADVANCED )
92     set_subcategory( SUBCAT_ADVANCED_MISC )
93
94     add_submodule ()
95         set_description( N_("GNU TLS server") )
96         set_capability( "tls server", 1 )
97         set_category( CAT_ADVANCED )
98         set_subcategory( SUBCAT_ADVANCED_MISC )
99         set_callbacks( OpenServer, CloseServer )
100
101         add_string ("gnutls-priorities", "NORMAL", PRIORITIES_TEXT,
102                     PRIORITIES_LONGTEXT, false)
103             change_string_list (priorities_values, priorities_text)
104 vlc_module_end ()
105
106 static vlc_mutex_t gnutls_mutex = VLC_STATIC_MUTEX;
107
108 /**
109  * Initializes GnuTLS with proper locking.
110  * @return VLC_SUCCESS on success, a VLC error code otherwise.
111  */
112 static int gnutls_Init (vlc_object_t *p_this)
113 {
114     int ret = VLC_EGENERIC;
115
116     vlc_gcrypt_init (); /* GnuTLS depends on gcrypt */
117
118     vlc_mutex_lock (&gnutls_mutex);
119     if (gnutls_global_init ())
120     {
121         msg_Err (p_this, "cannot initialize GnuTLS");
122         goto error;
123     }
124
125     const char *psz_version = gnutls_check_version ("2.0.0");
126     if (psz_version == NULL)
127     {
128         msg_Err (p_this, "unsupported GnuTLS version");
129         gnutls_global_deinit ();
130         goto error;
131     }
132
133     msg_Dbg (p_this, "GnuTLS v%s initialized", psz_version);
134     ret = VLC_SUCCESS;
135
136 error:
137     vlc_mutex_unlock (&gnutls_mutex);
138     return ret;
139 }
140
141
142 /**
143  * Deinitializes GnuTLS.
144  */
145 static void gnutls_Deinit (vlc_object_t *p_this)
146 {
147     vlc_mutex_lock (&gnutls_mutex);
148
149     gnutls_global_deinit ();
150     msg_Dbg (p_this, "GnuTLS deinitialized");
151     vlc_mutex_unlock (&gnutls_mutex);
152 }
153
154
155 static int gnutls_Error (vlc_object_t *obj, int val)
156 {
157     switch (val)
158     {
159         case GNUTLS_E_AGAIN:
160 #ifdef WIN32
161             WSASetLastError (WSAEWOULDBLOCK);
162 #else
163             errno = EAGAIN;
164 #endif
165             break;
166
167         case GNUTLS_E_INTERRUPTED:
168 #ifdef WIN32
169             WSASetLastError (WSAEINTR);
170 #else
171             errno = EINTR;
172 #endif
173             break;
174
175         default:
176             msg_Err (obj, "%s", gnutls_strerror (val));
177 #ifndef NDEBUG
178             if (!gnutls_error_is_fatal (val))
179                 msg_Err (obj, "Error above should be handled");
180 #endif
181 #ifdef WIN32
182             WSASetLastError (WSAECONNRESET);
183 #else
184             errno = ECONNRESET;
185 #endif
186     }
187     return -1;
188 }
189 #define gnutls_Error(o, val) gnutls_Error(VLC_OBJECT(o), val)
190
191
192 struct vlc_tls_sys
193 {
194     gnutls_session_t session;
195     gnutls_certificate_credentials_t x509_cred;
196     char *hostname;
197     bool handshaked;
198 };
199
200
201 /**
202  * Sends data through a TLS session.
203  */
204 static int gnutls_Send (void *opaque, const void *buf, size_t length)
205 {
206     vlc_tls_t *session = opaque;
207     vlc_tls_sys_t *sys = session->sys;
208
209     int val = gnutls_record_send (sys->session, buf, length);
210     return (val < 0) ? gnutls_Error (session, val) : val;
211 }
212
213
214 /**
215  * Receives data through a TLS session.
216  */
217 static int gnutls_Recv (void *opaque, void *buf, size_t length)
218 {
219     vlc_tls_t *session = opaque;
220     vlc_tls_sys_t *sys = session->sys;
221
222     int val = gnutls_record_recv (sys->session, buf, length);
223     return (val < 0) ? gnutls_Error (session, val) : val;
224 }
225
226
227 /**
228  * Starts or continues the TLS handshake.
229  *
230  * @return -1 on fatal error, 0 on successful handshake completion,
231  * 1 if more would-be blocking recv is needed,
232  * 2 if more would-be blocking send is required.
233  */
234 static int gnutls_ContinueHandshake (vlc_tls_t *session)
235 {
236     vlc_tls_sys_t *sys = session->sys;
237     int val;
238
239 #ifdef WIN32
240     WSASetLastError (0);
241 #endif
242     val = gnutls_handshake (sys->session);
243     if ((val == GNUTLS_E_AGAIN) || (val == GNUTLS_E_INTERRUPTED))
244         return 1 + gnutls_record_get_direction (sys->session);
245
246     if (val < 0)
247     {
248 #ifdef WIN32
249         msg_Dbg (session, "Winsock error %d", WSAGetLastError ());
250 #endif
251         msg_Err (session, "TLS handshake error: %s", gnutls_strerror (val));
252         return -1;
253     }
254
255     sys->handshaked = true;
256     return 0;
257 }
258
259
260 typedef struct
261 {
262     int flag;
263     const char *msg;
264 } error_msg_t;
265
266 static const error_msg_t cert_errors[] =
267 {
268     { GNUTLS_CERT_INVALID,
269         "Certificate could not be verified" },
270     { GNUTLS_CERT_REVOKED,
271         "Certificate was revoked" },
272     { GNUTLS_CERT_SIGNER_NOT_FOUND,
273         "Certificate's signer was not found" },
274     { GNUTLS_CERT_SIGNER_NOT_CA,
275         "Certificate's signer is not a CA" },
276     { GNUTLS_CERT_INSECURE_ALGORITHM,
277         "Insecure certificate signature algorithm" },
278     { GNUTLS_CERT_NOT_ACTIVATED,
279         "Certificate is not yet activated" },
280     { GNUTLS_CERT_EXPIRED,
281         "Certificate has expired" },
282     { 0, NULL }
283 };
284
285
286 static int gnutls_HandshakeAndValidate (vlc_tls_t *session)
287 {
288     vlc_tls_sys_t *sys = session->sys;
289
290     int val = gnutls_ContinueHandshake (session);
291     if (val)
292         return val;
293
294     /* certificates chain verification */
295     unsigned status;
296
297     val = gnutls_certificate_verify_peers2 (sys->session, &status);
298     if (val)
299     {
300         msg_Err (session, "Certificate verification failed: %s",
301                  gnutls_strerror (val));
302         return -1;
303     }
304
305     if (status)
306     {
307         msg_Err (session, "TLS session: access denied (status 0x%X)", status);
308         for (const error_msg_t *e = cert_errors; e->flag; e++)
309         {
310             if (status & e->flag)
311             {
312                 msg_Err (session, "%s", e->msg);
313                 status &= ~e->flag;
314             }
315         }
316
317         if (status)
318             msg_Err (session,
319                      "unknown certificate error (you found a bug in VLC)");
320         return -1;
321     }
322
323     /* certificate (host)name verification */
324     const gnutls_datum_t *data;
325     data = gnutls_certificate_get_peers (sys->session, &(unsigned){0});
326     if (data == NULL)
327     {
328         msg_Err (session, "Peer certificate not available");
329         return -1;
330     }
331
332     gnutls_x509_crt_t cert;
333     val = gnutls_x509_crt_init (&cert);
334     if (val)
335     {
336         msg_Err (session, "x509 fatal error: %s", gnutls_strerror (val));
337         return -1;
338     }
339
340     val = gnutls_x509_crt_import (cert, data, GNUTLS_X509_FMT_DER);
341     if (val)
342     {
343         msg_Err (session, "Certificate import error: %s",
344                  gnutls_strerror (val));
345         goto error;
346     }
347
348     if (sys->hostname != NULL
349      && !gnutls_x509_crt_check_hostname (cert, sys->hostname))
350     {
351         msg_Err (session, "Certificate does not match \"%s\"", sys->hostname);
352         goto error;
353     }
354
355     gnutls_x509_crt_deinit (cert);
356     msg_Dbg (session, "TLS/x509 certificate verified");
357     return 0;
358
359 error:
360     gnutls_x509_crt_deinit (cert);
361     return -1;
362 }
363
364 static int
365 gnutls_SessionPrioritize (vlc_object_t *obj, gnutls_session_t session)
366 {
367     char *priorities = var_InheritString (obj, "gnutls-priorities");
368     if (unlikely(priorities == NULL))
369         return VLC_ENOMEM;
370
371     const char *errp;
372     int val = gnutls_priority_set_direct (session, priorities, &errp);
373     if (val < 0)
374     {
375         msg_Err (obj, "cannot set TLS priorities \"%s\": %s", errp,
376                  gnutls_strerror (val));
377         val = VLC_EGENERIC;
378     }
379     else
380         val = VLC_SUCCESS;
381     free (priorities);
382     return val;
383 }
384
385 #ifndef WIN32
386 /**
387  * Loads x509 credentials from a file descriptor (directory or regular file)
388  * and closes the descriptor.
389  */
390 static void gnutls_x509_AddFD (vlc_object_t *obj,
391                                gnutls_certificate_credentials_t cred,
392                                int fd, bool priv, unsigned recursion)
393 {
394     DIR *dir = fdopendir (fd);
395     if (dir != NULL)
396     {
397         if (recursion == 0)
398             goto skipdir;
399         recursion--;
400
401         for (;;)
402         {
403             char *ent = vlc_readdir (dir);
404             if (ent == NULL)
405                 break;
406
407             if ((strcmp (ent, ".") == 0) || (strcmp (ent, "..") == 0))
408             {
409                 free (ent);
410                 continue;
411             }
412
413             int nfd = vlc_openat (fd, ent, O_RDONLY);
414             if (nfd != -1)
415             {
416                 msg_Dbg (obj, "loading x509 credentials from %s...", ent);
417                 gnutls_x509_AddFD (obj, cred, nfd, priv, recursion);
418             }
419             else
420                 msg_Dbg (obj, "cannot access x509 credentials in %s", ent);
421             free (ent);
422         }
423     skipdir:
424         closedir (dir);
425         return;
426     }
427
428     block_t *block = block_File (fd);
429     if (block != NULL)
430     {
431         gnutls_datum_t data = {
432             .data = block->p_buffer,
433             .size = block->i_buffer,
434         };
435         int res = priv
436             ? gnutls_certificate_set_x509_key_mem (cred, &data, &data,
437                                                    GNUTLS_X509_FMT_PEM)
438             : gnutls_certificate_set_x509_trust_mem (cred, &data,
439                                                      GNUTLS_X509_FMT_PEM);
440         block_Release (block);
441
442         if (res < 0)
443             msg_Warn (obj, "cannot add x509 credentials: %s",
444                       gnutls_strerror (res));
445         else
446             msg_Dbg (obj, "added %d %s(s)", res, priv ? "key" : "certificate");
447     }
448     else
449         msg_Warn (obj, "cannot read x509 credentials: %m");
450     close (fd);
451 }
452
453 static void gnutls_x509_AddPath (vlc_object_t *obj,
454                                  gnutls_certificate_credentials_t cred,
455                                  const char *path, bool priv)
456 {
457     msg_Dbg (obj, "loading x509 credentials in %s...", path);
458     int fd = vlc_open (path, O_RDONLY);
459     if (fd == -1)
460     {
461         msg_Warn (obj, "cannot access x509 in %s: %m", path);
462         return;
463     }
464
465     gnutls_x509_AddFD (obj, cred, fd, priv, 5);
466 }
467 #else /* WIN32 */
468 static int
469 gnutls_loadOSCAList (vlc_object_t *p_this,
470                      gnutls_certificate_credentials cred)
471 {
472     HCERTSTORE hCertStore = CertOpenSystemStoreA((HCRYPTPROV)NULL, "ROOT");
473     if (!hCertStore)
474     {
475         msg_Warn (p_this, "could not open the Cert SystemStore");
476         return VLC_EGENERIC;
477     }
478
479     PCCERT_CONTEXT pCertContext = CertEnumCertificatesInStore(hCertStore, NULL);
480     while( pCertContext )
481     {
482         gnutls_datum data = {
483             .data = pCertContext->pbCertEncoded,
484             .size = pCertContext->cbCertEncoded,
485         };
486
487         if(!gnutls_certificate_set_x509_trust_mem(cred, &data, GNUTLS_X509_FMT_DER))
488         {
489             msg_Warn (p_this, "cannot add x509 credential");
490             return VLC_EGENERIC;
491         }
492
493         pCertContext = CertEnumCertificatesInStore(hCertStore, pCertContext);
494     }
495     return VLC_SUCCESS;
496 }
497 #endif /* WIN32 */
498
499 /**
500  * Initializes a client-side TLS session.
501  */
502 static int OpenClient (vlc_tls_t *session, int fd, const char *hostname)
503 {
504     if (gnutls_Init (VLC_OBJECT(session)))
505         return VLC_EGENERIC;
506
507     vlc_tls_sys_t *sys = malloc (sizeof (*sys));
508     if (unlikely(sys == NULL))
509     {
510         gnutls_Deinit (VLC_OBJECT(session));
511         return VLC_ENOMEM;
512     }
513
514     session->sys = sys;
515     session->sock.p_sys = session;
516     session->sock.pf_send = gnutls_Send;
517     session->sock.pf_recv = gnutls_Recv;
518     sys->handshaked = false;
519
520     int val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
521     if (val != 0)
522     {
523         msg_Err (session, "cannot allocate X509 credentials: %s",
524                  gnutls_strerror (val));
525         goto error;
526     }
527
528 #ifndef WIN32
529     char *userdir = config_GetUserDir (VLC_DATA_DIR);
530     if (userdir != NULL)
531     {
532         char path[strlen (userdir) + sizeof ("/ssl/private/")];
533         sprintf (path, "%s/ssl", userdir);
534         vlc_mkdir (path, 0755);
535
536         sprintf (path, "%s/ssl/certs/", userdir);
537         gnutls_x509_AddPath (VLC_OBJECT(session), sys->x509_cred, path, false);
538         sprintf (path, "%s/ssl/private/", userdir);
539         gnutls_x509_AddPath (VLC_OBJECT(session), sys->x509_cred, path, true);
540         free (userdir);
541     }
542
543     const char *confdir = config_GetConfDir ();
544     {
545         char path[strlen (confdir)
546                    + sizeof ("/ssl/certs/ca-certificates.crt")];
547         sprintf (path, "%s/ssl/certs/ca-certificates.crt", confdir);
548         gnutls_x509_AddPath (VLC_OBJECT(session), sys->x509_cred, path, false);
549     }
550 #else /* WIN32 */
551     gnutls_loadOSCAList (VLC_OBJECT(session), sys->x509_cred);
552 #endif
553     gnutls_certificate_set_verify_flags (sys->x509_cred,
554                                          GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
555
556     session->handshake = gnutls_HandshakeAndValidate;
557     /*session->_handshake = gnutls_ContinueHandshake;*/
558
559     val = gnutls_init (&sys->session, GNUTLS_CLIENT);
560     if (val != 0)
561     {
562         msg_Err (session, "cannot initialize TLS session: %s",
563                  gnutls_strerror (val));
564         gnutls_certificate_free_credentials (sys->x509_cred);
565         goto error;
566     }
567
568     if (gnutls_SessionPrioritize (VLC_OBJECT(session), sys->session))
569         goto s_error;
570
571     /* minimum DH prime bits */
572     gnutls_dh_set_prime_bits (sys->session, 1024);
573
574     val = gnutls_credentials_set (sys->session, GNUTLS_CRD_CERTIFICATE,
575                                   sys->x509_cred);
576     if (val < 0)
577     {
578         msg_Err (session, "cannot set TLS session credentials: %s",
579                  gnutls_strerror (val));
580         goto s_error;
581     }
582
583     /* server name */
584     if (likely(hostname != NULL))
585     {
586         /* fill Server Name Indication */
587         gnutls_server_name_set (sys->session, GNUTLS_NAME_DNS,
588                                 hostname, strlen (hostname));
589         /* keep hostname to match CNAME after handshake */
590         sys->hostname = strdup (hostname);
591         if (unlikely(sys->hostname == NULL))
592             goto s_error;
593     }
594     else
595         sys->hostname = NULL;
596
597     gnutls_transport_set_ptr (sys->session,
598                               (gnutls_transport_ptr_t)(intptr_t)fd);
599     return VLC_SUCCESS;
600
601 s_error:
602     gnutls_deinit (sys->session);
603     gnutls_certificate_free_credentials (sys->x509_cred);
604 error:
605     gnutls_Deinit (VLC_OBJECT(session));
606     free (sys);
607     return VLC_EGENERIC;
608 }
609
610
611 static void CloseClient (vlc_tls_t *session)
612 {
613     vlc_tls_sys_t *sys = session->sys;
614
615     if (sys->handshaked)
616         gnutls_bye (sys->session, GNUTLS_SHUT_WR);
617     gnutls_deinit (sys->session);
618     /* credentials must be free'd *after* gnutls_deinit() */
619     gnutls_certificate_free_credentials (sys->x509_cred);
620
621     gnutls_Deinit (VLC_OBJECT(session));
622     free (sys->hostname);
623     free (sys);
624 }
625
626
627 /**
628  * Server-side TLS
629  */
630 struct vlc_tls_creds_sys
631 {
632     gnutls_certificate_credentials_t x509_cred;
633     gnutls_dh_params_t               dh_params;
634     int                            (*handshake) (vlc_tls_t *);
635 };
636
637
638 /**
639  * Terminates TLS session and releases session data.
640  * You still have to close the socket yourself.
641  */
642 static void gnutls_SessionClose (vlc_tls_t *session)
643 {
644     vlc_tls_sys_t *sys = session->sys;
645
646     if (sys->handshaked)
647         gnutls_bye (sys->session, GNUTLS_SHUT_WR);
648     gnutls_deinit (sys->session);
649
650     vlc_object_release (session);
651     free (sys);
652 }
653
654
655 /**
656  * Initializes a server-side TLS session.
657  */
658 static vlc_tls_t *gnutls_SessionOpen (vlc_tls_creds_t *server, int fd)
659 {
660     vlc_tls_creds_sys_t *ssys = server->sys;
661     int val;
662
663     vlc_tls_t *session = vlc_object_create (server, sizeof (*session));
664     if (unlikely(session == NULL))
665         return NULL;
666
667     vlc_tls_sys_t *sys = malloc (sizeof (*session->sys));
668     if (unlikely(sys == NULL))
669     {
670         vlc_object_release (session);
671         return NULL;
672     }
673
674     session->sys = sys;
675     session->sock.p_sys = session;
676     session->sock.pf_send = gnutls_Send;
677     session->sock.pf_recv = gnutls_Recv;
678     session->handshake = ssys->handshake;
679     session->u.close = gnutls_SessionClose;
680     sys->handshaked = false;
681     sys->hostname = NULL;
682
683     val = gnutls_init (&sys->session, GNUTLS_SERVER);
684     if (val != 0)
685     {
686         msg_Err (server, "cannot initialize TLS session: %s",
687                  gnutls_strerror (val));
688         free (sys);
689         vlc_object_release (session);
690         return NULL;
691     }
692
693     if (gnutls_SessionPrioritize (VLC_OBJECT (server), sys->session))
694         goto error;
695
696     val = gnutls_credentials_set (sys->session, GNUTLS_CRD_CERTIFICATE,
697                                   ssys->x509_cred);
698     if (val < 0)
699     {
700         msg_Err (server, "cannot set TLS session credentials: %s",
701                  gnutls_strerror (val));
702         goto error;
703     }
704
705     if (session->handshake == gnutls_HandshakeAndValidate)
706         gnutls_certificate_server_set_request (sys->session,
707                                                GNUTLS_CERT_REQUIRE);
708
709     gnutls_transport_set_ptr (sys->session,
710                               (gnutls_transport_ptr_t)(intptr_t)fd);
711     return session;
712
713 error:
714     gnutls_SessionClose (session);
715     return NULL;
716 }
717
718
719 /**
720  * Adds one or more certificate authorities.
721  *
722  * @param ca_path (Unicode) path to an x509 certificates list.
723  *
724  * @return -1 on error, 0 on success.
725  */
726 static int gnutls_ServerAddCA (vlc_tls_creds_t *server, const char *ca_path)
727 {
728     vlc_tls_creds_sys_t *sys = server->sys;
729     const char *local_path = ToLocale (ca_path);
730
731     int val = gnutls_certificate_set_x509_trust_file (sys->x509_cred,
732                                                       local_path,
733                                                       GNUTLS_X509_FMT_PEM );
734     LocaleFree (local_path);
735     if (val < 0)
736     {
737         msg_Err (server, "cannot add trusted CA (%s): %s", ca_path,
738                  gnutls_strerror (val));
739         return VLC_EGENERIC;
740     }
741     msg_Dbg (server, " %d trusted CA added (%s)", val, ca_path);
742
743     /* enables peer's certificate verification */
744     sys->handshake = gnutls_HandshakeAndValidate;
745
746     return VLC_SUCCESS;
747 }
748
749
750 /**
751  * Adds a certificates revocation list to be sent to TLS clients.
752  *
753  * @param crl_path (Unicode) path of the CRL file.
754  *
755  * @return -1 on error, 0 on success.
756  */
757 static int gnutls_ServerAddCRL (vlc_tls_creds_t *server, const char *crl_path)
758 {
759     vlc_tls_creds_sys_t *sys = server->sys;
760     const char *local_path = ToLocale (crl_path);
761
762     int val = gnutls_certificate_set_x509_crl_file (sys->x509_cred,
763                                                     local_path,
764                                                     GNUTLS_X509_FMT_PEM);
765     LocaleFree (local_path);
766     if (val < 0)
767     {
768         msg_Err (server, "cannot add CRL (%s): %s", crl_path,
769                  gnutls_strerror (val));
770         return VLC_EGENERIC;
771     }
772     msg_Dbg (server, "%d CRL added (%s)", val, crl_path);
773     return VLC_SUCCESS;
774 }
775
776
777 /**
778  * Allocates a whole server's TLS credentials.
779  */
780 static int OpenServer (vlc_object_t *obj)
781 {
782     vlc_tls_creds_t *server = (vlc_tls_creds_t *)obj;
783     int val;
784
785     if (gnutls_Init (obj))
786         return VLC_EGENERIC;
787
788     msg_Dbg (obj, "creating TLS server");
789
790     vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
791     if (unlikely(sys == NULL))
792         return VLC_ENOMEM;
793
794     server->sys     = sys;
795     server->add_CA  = gnutls_ServerAddCA;
796     server->add_CRL = gnutls_ServerAddCRL;
797     server->open    = gnutls_SessionOpen;
798     /* No certificate validation by default */
799     sys->handshake  = gnutls_ContinueHandshake;
800
801     /* Sets server's credentials */
802     val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
803     if (val != 0)
804     {
805         msg_Err (server, "cannot allocate X509 credentials: %s",
806                  gnutls_strerror (val));
807         goto error;
808     }
809
810     char *cert_path = var_GetNonEmptyString (obj, "tls-x509-cert");
811     char *key_path = var_GetNonEmptyString (obj, "tls-x509-key");
812     const char *lcert = ToLocale (cert_path);
813     const char *lkey = ToLocale (key_path);
814     val = gnutls_certificate_set_x509_key_file (sys->x509_cred, lcert, lkey,
815                                                 GNUTLS_X509_FMT_PEM);
816     LocaleFree (lkey);
817     LocaleFree (lcert);
818     free (key_path);
819     free (cert_path);
820
821     if (val < 0)
822     {
823         msg_Err (server, "cannot set certificate chain or private key: %s",
824                  gnutls_strerror (val));
825         gnutls_certificate_free_credentials (sys->x509_cred);
826         goto error;
827     }
828
829     /* FIXME:
830      * - support other cipher suites
831      */
832     val = gnutls_dh_params_init (&sys->dh_params);
833     if (val >= 0)
834     {
835         const gnutls_datum_t data = {
836             .data = (unsigned char *)dh_params,
837             .size = sizeof (dh_params) - 1,
838         };
839
840         val = gnutls_dh_params_import_pkcs3 (sys->dh_params, &data,
841                                              GNUTLS_X509_FMT_PEM);
842         if (val == 0)
843             gnutls_certificate_set_dh_params (sys->x509_cred,
844                                               sys->dh_params);
845     }
846     if (val < 0)
847     {
848         msg_Err (server, "cannot initialize DHE cipher suites: %s",
849                  gnutls_strerror (val));
850     }
851
852     return VLC_SUCCESS;
853
854 error:
855     free (sys);
856     return VLC_EGENERIC;
857 }
858
859 /**
860  * Destroys a TLS server object.
861  */
862 static void CloseServer (vlc_object_t *obj)
863 {
864     vlc_tls_creds_t *server = (vlc_tls_creds_t *)obj;
865     vlc_tls_creds_sys_t *sys = server->sys;
866
867     /* all sessions depending on the server are now deinitialized */
868     gnutls_certificate_free_credentials (sys->x509_cred);
869     gnutls_dh_params_deinit (sys->dh_params);
870     free (sys);
871
872     gnutls_Deinit (obj);
873 }