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