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