tls: add ALPN parameters
[vlc.git] / 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 /*** TLS  session ***/
147
148 vlc_tls_t *vlc_tls_SessionCreate (vlc_tls_creds_t *crd, int fd,
149                                   const char *host, const char *const *alpn)
150 {
151     vlc_tls_t *session = vlc_custom_create (crd, sizeof (*session),
152                                             "tls session");
153     int val = crd->open (crd, session, fd, host, alpn);
154     if (val == VLC_SUCCESS)
155         return session;
156     vlc_object_release (session);
157     return NULL;
158 }
159
160 int vlc_tls_SessionHandshake (vlc_tls_t *session, const char *host,
161                               const char *service, char **restrict alp)
162 {
163     vlc_tls_creds_t *crd = (vlc_tls_creds_t *)(session->p_parent);
164
165     return crd->handshake (session, host, service, alp);
166 }
167
168 void vlc_tls_SessionDelete (vlc_tls_t *session)
169 {
170     vlc_tls_creds_t *crd = (vlc_tls_creds_t *)(session->p_parent);
171
172     crd->close (session);
173     vlc_object_release (session);
174 }
175
176 /**
177  * Performs client side of TLS handshake through a connected socket, and
178  * establishes a secure channel. This is a blocking network operation.
179  *
180  * @param fd socket through which to establish the secure channel
181  * @param hostname expected server name, used both as Server Name Indication
182  *                 and as expected Common Name of the peer certificate
183  * @param service unique identifier for the service to connect to
184  *                (only used locally for certificates database)
185  * @param alpn NULL-terminated list of Application Layer Protocols
186  *             to negotiate, or NULL to not negotiate protocols
187  * @param alp storage space for the negotiated Application Layer
188  *            Protocol or NULL if negotiation was not performed[OUT]
189  *
190  * @return NULL on error.
191  **/
192 vlc_tls_t *vlc_tls_ClientSessionCreate (vlc_tls_creds_t *crd, int fd,
193                                         const char *host, const char *service,
194                                         const char *const *alpn, char **alp)
195 {
196     vlc_tls_t *session = vlc_tls_SessionCreate (crd, fd, host, alpn);
197     if (session == NULL)
198         return NULL;
199
200     mtime_t deadline = mdate ();
201     deadline += var_InheritInteger (crd, "ipv4-timeout") * 1000;
202
203     struct pollfd ufd[1];
204     ufd[0].fd = fd;
205
206     int val;
207     while ((val = vlc_tls_SessionHandshake (session, host, service, alp)) != 0)
208     {
209         if (val < 0)
210         {
211             msg_Err (session, "TLS client session handshake error");
212             goto error;
213         }
214
215         mtime_t now = mdate ();
216         if (now > deadline)
217            now = deadline;
218
219         assert (val <= 2);
220         ufd[0] .events = (val == 1) ? POLLIN : POLLOUT;
221
222         if (poll (ufd, 1, (deadline - now) / 1000) == 0)
223         {
224             msg_Err (session, "TLS client session handshake timeout");
225             goto error;
226         }
227     }
228     return session;
229 error:
230     vlc_tls_SessionDelete (session);
231     return NULL;
232 }