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