]> git.sesse.net Git - vlc/blob - src/network/tls.c
vout: use atomic variables for stats instead of spin lock
[vlc] / src / network / tls.c
1 /*****************************************************************************
2  * tls.c
3  *****************************************************************************
4  * Copyright © 2004-2007 Rémi Denis-Courmont
5  * $Id$
6  *
7  * Authors: Rémi Denis-Courmont <rem # videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /**
25  * @file
26  * libvlc interface to the Transport Layer Security (TLS) plugins.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #ifdef HAVE_POLL
34 # include <poll.h>
35 #endif
36 #include <assert.h>
37
38 #include <vlc_common.h>
39 #include "libvlc.h"
40
41 #include <vlc_tls.h>
42 #include <vlc_modules.h>
43
44 /*** TLS credentials ***/
45
46 static int tls_server_load(void *func, va_list ap)
47 {
48     int (*activate) (vlc_tls_creds_t *, const char *, const char *) = func;
49     vlc_tls_creds_t *crd = va_arg (ap, vlc_tls_creds_t *);
50     const char *cert = va_arg (ap, const char *);
51     const char *key = va_arg (ap, const char *);
52
53     return activate (crd, cert, key);
54 }
55
56 static int tls_client_load(void *func, va_list ap)
57 {
58     int (*activate) (vlc_tls_creds_t *) = func;
59     vlc_tls_creds_t *crd = va_arg (ap, vlc_tls_creds_t *);
60
61     return activate (crd);
62 }
63
64 static void tls_unload(void *func, va_list ap)
65 {
66     void (*deactivate) (vlc_tls_creds_t *) = func;
67     vlc_tls_creds_t *crd = va_arg (ap, vlc_tls_creds_t *);
68
69     deactivate (crd);
70 }
71
72 /**
73  * Allocates a whole server's TLS credentials.
74  *
75  * @param cert_path required (Unicode) path to an x509 certificate,
76  *                  if NULL, anonymous key exchange will be used.
77  * @param key_path (UTF-8) path to the PKCS private key for the certificate,
78  *                 if NULL; cert_path will be used.
79  *
80  * @return NULL on error.
81  */
82 vlc_tls_creds_t *
83 vlc_tls_ServerCreate (vlc_object_t *obj, const char *cert_path,
84                       const char *key_path)
85 {
86     vlc_tls_creds_t *srv = vlc_custom_create (obj, sizeof (*srv),
87                                               "tls server");
88     if (unlikely(srv == NULL))
89         return NULL;
90
91     if (key_path == NULL)
92         key_path = cert_path;
93
94     srv->module = vlc_module_load (srv, "tls server", NULL, false,
95                                    tls_server_load, srv, cert_path, key_path);
96     if (srv->module == NULL)
97     {
98         msg_Err (srv, "TLS server plugin not available");
99         vlc_object_release (srv);
100         return NULL;
101     }
102
103     return srv;
104 }
105
106 /**
107  * Allocates TLS credentials for a client.
108  * Credentials can be cached and reused across multiple TLS sessions.
109  *
110  * @return TLS credentials object, or NULL on error.
111  **/
112 vlc_tls_creds_t *vlc_tls_ClientCreate (vlc_object_t *obj)
113 {
114     vlc_tls_creds_t *crd = vlc_custom_create (obj, sizeof (*crd),
115                                               "tls client");
116     if (unlikely(crd == NULL))
117         return NULL;
118
119     crd->module = vlc_module_load (crd, "tls client", NULL, false,
120                                    tls_client_load, crd);
121     if (crd->module == NULL)
122     {
123         msg_Err (crd, "TLS client plugin not available");
124         vlc_object_release (crd);
125         return NULL;
126     }
127
128     return crd;
129 }
130
131 /**
132  * Releases data allocated with vlc_tls_ClientCreate() or
133  * vlc_tls_ServerCreate().
134  * @param srv TLS server object to be destroyed, or NULL
135  */
136 void vlc_tls_Delete (vlc_tls_creds_t *crd)
137 {
138     if (crd == NULL)
139         return;
140
141     vlc_module_unload (crd->module, tls_unload, crd);
142     vlc_object_release (crd);
143 }
144
145
146 /**
147  * Adds one or more certificate authorities from a file.
148  * @return -1 on error, 0 on success.
149  */
150 int vlc_tls_ServerAddCA (vlc_tls_creds_t *srv, const char *path)
151 {
152     return srv->add_CA (srv, path);
153 }
154
155
156 /**
157  * Adds one or more certificate revocation list from a file.
158  * @return -1 on error, 0 on success.
159  */
160 int vlc_tls_ServerAddCRL (vlc_tls_creds_t *srv, const char *path)
161 {
162     return srv->add_CRL (srv, path);
163 }
164
165
166 /*** TLS  session ***/
167
168 vlc_tls_t *vlc_tls_SessionCreate (vlc_tls_creds_t *crd, int fd,
169                                   const char *host)
170 {
171     vlc_tls_t *session = vlc_custom_create (crd, sizeof (*session),
172                                             "tls session");
173     int val = crd->open (crd, session, fd, host);
174     if (val == VLC_SUCCESS)
175         return session;
176     vlc_object_release (session);
177     return NULL;
178 }
179
180 void vlc_tls_SessionDelete (vlc_tls_t *session)
181 {
182     vlc_tls_creds_t *crd = (vlc_tls_creds_t *)(session->p_parent);
183
184     crd->close (crd, session);
185     vlc_object_release (session);
186 }
187
188 int vlc_tls_SessionHandshake (vlc_tls_t *session, const char *host,
189                               const char *service)
190 {
191     return session->handshake (session, host, service);
192 }
193
194 /**
195  * Performs client side of TLS handshake through a connected socket, and
196  * establishes a secure channel. This is a blocking network operation.
197  *
198  * @param fd socket through which to establish the secure channel
199  * @param hostname expected server name, used both as Server Name Indication
200  *                 and as expected Common Name of the peer certificate
201  *
202  * @return NULL on error.
203  **/
204 vlc_tls_t *vlc_tls_ClientSessionCreate (vlc_tls_creds_t *crd, int fd,
205                                         const char *host, const char *service)
206 {
207     vlc_tls_t *session = vlc_tls_SessionCreate (crd, fd, host);
208     if (session == NULL)
209         return NULL;
210
211     mtime_t deadline = mdate ();
212     deadline += var_InheritInteger (crd, "ipv4-timeout") * 1000;
213
214     struct pollfd ufd[1];
215     ufd[0].fd = fd;
216
217     int val;
218     while ((val = vlc_tls_SessionHandshake (session, host, service)) > 0)
219     {
220         mtime_t now = mdate ();
221         if (now > deadline)
222            now = deadline;
223
224         assert (val <= 2);
225         ufd[0] .events = (val == 1) ? POLLIN : POLLOUT;
226
227         if (poll (ufd, 1, (deadline - now) / 1000) == 0)
228         {
229             msg_Err (session, "TLS client session handshake timeout");
230             val = -1;
231             break;
232         }
233     }
234     while (val > 0);
235
236     if (val != 0)
237     {
238         msg_Err (session, "TLS client session handshake error");
239         vlc_tls_SessionDelete (session);
240         session = NULL;
241     }
242     return session;
243 }