demux: ts: fix potential null deref (cid #1412981)
[vlc.git] / src / network / tls.c
blob56e04d6ce75436775c9a91a3d3004a53a312037d
1 /*****************************************************************************
2 * tls.c
3 *****************************************************************************
4 * Copyright © 2004-2016 Rémi Denis-Courmont
5 * $Id$
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20 *****************************************************************************/
22 /**
23 * @file
24 * Transport Layer Socket abstraction.
26 * This file implements the Transport Layer Socket (vlc_tls) abstraction.
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #ifdef HAVE_POLL
34 # include <poll.h>
35 #endif
36 #include <assert.h>
37 #include <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #ifdef HAVE_SYS_UIO_H
42 # include <sys/uio.h>
43 #endif
44 #ifdef HAVE_NETINET_TCP_H
45 # include <netinet/tcp.h>
46 #endif
47 #ifndef SOL_TCP
48 # define SOL_TCP IPPROTO_TCP
49 #endif
51 #include <vlc_common.h>
52 #include "libvlc.h"
54 #include <vlc_tls.h>
55 #include <vlc_modules.h>
56 #include <vlc_interrupt.h>
58 /*** TLS credentials ***/
60 static int tls_server_load(void *func, va_list ap)
62 int (*activate) (vlc_tls_creds_t *, const char *, const char *) = func;
63 vlc_tls_creds_t *crd = va_arg (ap, vlc_tls_creds_t *);
64 const char *cert = va_arg (ap, const char *);
65 const char *key = va_arg (ap, const char *);
67 return activate (crd, cert, key);
70 static int tls_client_load(void *func, va_list ap)
72 int (*activate) (vlc_tls_creds_t *) = func;
73 vlc_tls_creds_t *crd = va_arg (ap, vlc_tls_creds_t *);
75 return activate (crd);
78 static void tls_unload(void *func, va_list ap)
80 void (*deactivate) (vlc_tls_creds_t *) = func;
81 vlc_tls_creds_t *crd = va_arg (ap, vlc_tls_creds_t *);
83 deactivate (crd);
86 vlc_tls_creds_t *
87 vlc_tls_ServerCreate (vlc_object_t *obj, const char *cert_path,
88 const char *key_path)
90 vlc_tls_creds_t *srv = vlc_custom_create (obj, sizeof (*srv),
91 "tls server");
92 if (unlikely(srv == NULL))
93 return NULL;
95 if (key_path == NULL)
96 key_path = cert_path;
98 srv->module = vlc_module_load (srv, "tls server", NULL, false,
99 tls_server_load, srv, cert_path, key_path);
100 if (srv->module == NULL)
102 msg_Err (srv, "TLS server plugin not available");
103 vlc_object_release (srv);
104 return NULL;
107 return srv;
110 vlc_tls_creds_t *vlc_tls_ClientCreate (vlc_object_t *obj)
112 vlc_tls_creds_t *crd = vlc_custom_create (obj, sizeof (*crd),
113 "tls client");
114 if (unlikely(crd == NULL))
115 return NULL;
117 crd->module = vlc_module_load (crd, "tls client", NULL, false,
118 tls_client_load, crd);
119 if (crd->module == NULL)
121 msg_Err (crd, "TLS client plugin not available");
122 vlc_object_release (crd);
123 return NULL;
126 return crd;
129 void vlc_tls_Delete (vlc_tls_creds_t *crd)
131 if (crd == NULL)
132 return;
134 vlc_module_unload(crd, crd->module, tls_unload, crd);
135 vlc_object_release (crd);
139 /*** TLS session ***/
141 static vlc_tls_t *vlc_tls_SessionCreate(vlc_tls_creds_t *crd,
142 vlc_tls_t *sock,
143 const char *host,
144 const char *const *alpn)
146 vlc_tls_t *session;
147 int canc = vlc_savecancel();
148 session = crd->open(crd, sock, host, alpn);
149 vlc_restorecancel(canc);
150 if (session != NULL)
151 session->p = sock;
152 return session;
155 void vlc_tls_SessionDelete (vlc_tls_t *session)
157 int canc = vlc_savecancel();
158 session->close(session);
159 vlc_restorecancel(canc);
162 static void cleanup_tls(void *data)
164 vlc_tls_t *session = data;
166 vlc_tls_SessionDelete (session);
169 #undef vlc_tls_ClientSessionCreate
170 vlc_tls_t *vlc_tls_ClientSessionCreate(vlc_tls_creds_t *crd, vlc_tls_t *sock,
171 const char *host, const char *service,
172 const char *const *alpn, char **alp)
174 int val;
176 vlc_tls_t *session = vlc_tls_SessionCreate(crd, sock, host, alpn);
177 if (session == NULL)
178 return NULL;
180 int canc = vlc_savecancel();
181 mtime_t deadline = mdate ();
182 deadline += var_InheritInteger (crd, "ipv4-timeout") * 1000;
184 struct pollfd ufd[1];
185 ufd[0].fd = vlc_tls_GetFD(sock);
187 vlc_cleanup_push (cleanup_tls, session);
188 while ((val = crd->handshake(crd, session, host, service, alp)) != 0)
190 if (val < 0 || vlc_killed() )
192 if (val < 0)
193 msg_Err(crd, "TLS session handshake error");
194 error:
195 vlc_tls_SessionDelete (session);
196 session = NULL;
197 break;
200 mtime_t now = mdate ();
201 if (now > deadline)
202 now = deadline;
204 assert (val <= 2);
205 ufd[0] .events = (val == 1) ? POLLIN : POLLOUT;
207 vlc_restorecancel(canc);
208 val = vlc_poll_i11e(ufd, 1, (deadline - now) / 1000);
209 canc = vlc_savecancel();
210 if (val == 0)
212 msg_Err(crd, "TLS session handshake timeout");
213 goto error;
216 vlc_cleanup_pop();
217 vlc_restorecancel(canc);
218 return session;
221 vlc_tls_t *vlc_tls_ServerSessionCreate(vlc_tls_creds_t *crd,
222 vlc_tls_t *sock,
223 const char *const *alpn)
225 return vlc_tls_SessionCreate(crd, sock, NULL, alpn);
228 ssize_t vlc_tls_Read(vlc_tls_t *session, void *buf, size_t len, bool waitall)
230 struct pollfd ufd;
231 struct iovec iov;
233 ufd.fd = vlc_tls_GetFD(session);
234 ufd.events = POLLIN;
235 iov.iov_base = buf;
236 iov.iov_len = len;
238 for (size_t rcvd = 0;;)
240 if (vlc_killed())
242 errno = EINTR;
243 return -1;
246 ssize_t val = session->readv(session, &iov, 1);
247 if (val > 0)
249 if (!waitall)
250 return val;
251 iov.iov_base = (char *)iov.iov_base + val;
252 iov.iov_len -= val;
253 rcvd += val;
255 if (iov.iov_len == 0 || val == 0)
256 return rcvd;
257 if (val == -1)
259 if (vlc_killed())
260 return -1;
261 if (errno != EINTR && errno != EAGAIN)
262 return rcvd ? (ssize_t)rcvd : -1;
265 vlc_poll_i11e(&ufd, 1, -1);
269 ssize_t vlc_tls_Write(vlc_tls_t *session, const void *buf, size_t len)
271 struct pollfd ufd;
272 struct iovec iov;
274 ufd.fd = vlc_tls_GetFD(session);
275 ufd.events = POLLOUT;
276 iov.iov_base = (void *)buf;
277 iov.iov_len = len;
279 for (size_t sent = 0;;)
281 if (vlc_killed())
283 errno = EINTR;
284 return -1;
287 ssize_t val = session->writev(session, &iov, 1);
288 if (val > 0)
290 iov.iov_base = ((char *)iov.iov_base) + val;
291 iov.iov_len -= val;
292 sent += val;
294 if (iov.iov_len == 0 || val == 0)
295 return sent;
296 if (val == -1)
298 if (vlc_killed())
299 return -1;
300 if (errno != EINTR && errno != EAGAIN)
301 return sent ? (ssize_t)sent : -1;
304 vlc_poll_i11e(&ufd, 1, -1);
308 char *vlc_tls_GetLine(vlc_tls_t *session)
310 char *line = NULL;
311 size_t linelen = 0, linesize = 0;
315 if (linelen == linesize)
317 linesize += 1024;
319 char *newline = realloc(line, linesize);
320 if (unlikely(newline == NULL))
321 goto error;
322 line = newline;
325 if (vlc_tls_Read(session, line + linelen, 1, false) <= 0)
326 goto error;
328 while (line[linelen++] != '\n');
330 if (linelen >= 2 && line[linelen - 2] == '\r')
331 line[linelen - 2] = '\0';
332 return line;
334 error:
335 free(line);
336 return NULL;
339 typedef struct vlc_tls_socket
341 struct vlc_tls tls;
342 int fd;
343 socklen_t peerlen;
344 struct sockaddr peer[];
345 } vlc_tls_socket_t;
347 static int vlc_tls_SocketGetFD(vlc_tls_t *tls)
349 vlc_tls_socket_t *sock = (struct vlc_tls_socket *)tls;
351 return sock->fd;
354 static ssize_t vlc_tls_SocketRead(vlc_tls_t *tls, struct iovec *iov,
355 unsigned count)
357 struct msghdr msg =
359 .msg_iov = iov,
360 .msg_iovlen = count,
363 return recvmsg(vlc_tls_SocketGetFD(tls), &msg, 0);
366 static ssize_t vlc_tls_SocketWrite(vlc_tls_t *tls, const struct iovec *iov,
367 unsigned count)
369 const struct msghdr msg =
371 .msg_iov = (struct iovec *)iov,
372 .msg_iovlen = count,
375 return sendmsg(vlc_tls_SocketGetFD(tls), &msg, MSG_NOSIGNAL);
378 static int vlc_tls_SocketShutdown(vlc_tls_t *tls, bool duplex)
380 return shutdown(vlc_tls_SocketGetFD(tls), duplex ? SHUT_RDWR : SHUT_WR);
383 static void vlc_tls_SocketClose(vlc_tls_t *tls)
385 net_Close(vlc_tls_SocketGetFD(tls));
386 free(tls);
389 static vlc_tls_t *vlc_tls_SocketAlloc(int fd,
390 const struct sockaddr *restrict peer,
391 socklen_t peerlen)
393 vlc_tls_socket_t *sock = malloc(sizeof (*sock) + peerlen);
394 if (unlikely(sock == NULL))
395 return NULL;
397 vlc_tls_t *tls = &sock->tls;
399 tls->get_fd = vlc_tls_SocketGetFD;
400 tls->readv = vlc_tls_SocketRead;
401 tls->writev = vlc_tls_SocketWrite;
402 tls->shutdown = vlc_tls_SocketShutdown;
403 tls->close = vlc_tls_SocketClose;
404 tls->p = NULL;
406 sock->fd = fd;
407 sock->peerlen = peerlen;
408 if (peerlen > 0)
409 memcpy(sock->peer, peer, peerlen);
410 return tls;
413 vlc_tls_t *vlc_tls_SocketOpen(int fd)
415 return vlc_tls_SocketAlloc(fd, NULL, 0);
418 int vlc_tls_SocketPair(int family, int protocol, vlc_tls_t *pair[2])
420 int fds[2];
422 if (vlc_socketpair(family, SOCK_STREAM, protocol, fds, true))
423 return -1;
425 for (size_t i = 0; i < 2; i++)
427 setsockopt(fds[i], SOL_SOCKET, SO_REUSEADDR,
428 &(int){ 1 }, sizeof (int));
430 pair[i] = vlc_tls_SocketAlloc(fds[i], NULL, 0);
431 if (unlikely(pair[i] == NULL))
433 net_Close(fds[i]);
434 if (i)
435 vlc_tls_SessionDelete(pair[0]);
436 else
437 net_Close(fds[1]);
438 return -1;
441 return 0;
445 * Allocates an unconnected transport layer socket.
447 static vlc_tls_t *vlc_tls_SocketAddrInfo(const struct addrinfo *restrict info)
449 int fd = vlc_socket(info->ai_family, info->ai_socktype, info->ai_protocol,
450 true /* nonblocking */);
451 if (fd == -1)
452 return NULL;
454 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof (int));
456 if (info->ai_socktype == SOCK_STREAM && info->ai_protocol == IPPROTO_TCP)
457 setsockopt(fd, SOL_TCP, TCP_NODELAY, &(int){ 1 }, sizeof (int));
459 vlc_tls_t *sk = vlc_tls_SocketAlloc(fd, info->ai_addr, info->ai_addrlen);
460 if (unlikely(sk == NULL))
461 net_Close(fd);
462 return sk;
466 * Waits for pending transport layer socket connection.
468 static int vlc_tls_WaitConnect(vlc_tls_t *tls)
470 const int fd = vlc_tls_GetFD(tls);
471 struct pollfd ufd;
473 ufd.fd = fd;
474 ufd.events = POLLOUT;
478 if (vlc_killed())
480 errno = EINTR;
481 return -1;
484 while (vlc_poll_i11e(&ufd, 1, -1) <= 0);
486 int val;
487 socklen_t len = sizeof (val);
489 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len))
490 return -1;
492 if (val != 0)
494 errno = val;
495 return -1;
497 return 0;
501 * Connects a transport layer socket.
503 static ssize_t vlc_tls_Connect(vlc_tls_t *tls)
505 const vlc_tls_socket_t *sock = (vlc_tls_socket_t *)tls;
507 if (connect(sock->fd, sock->peer, sock->peerlen) == 0)
508 return 0;
509 #ifndef _WIN32
510 if (errno != EINPROGRESS)
511 return -1;
512 #else
513 if (WSAGetLastError() != WSAEWOULDBLOCK)
514 return -1;
515 #endif
516 return vlc_tls_WaitConnect(tls);
519 /* Callback for combined connection establishment and initial send */
520 static ssize_t vlc_tls_ConnectWrite(vlc_tls_t *tls,
521 const struct iovec *iov,unsigned count)
523 #ifdef MSG_FASTOPEN
524 vlc_tls_socket_t *sock = (vlc_tls_socket_t *)tls;
525 const struct msghdr msg =
527 .msg_name = sock->peer,
528 .msg_namelen = sock->peerlen,
529 .msg_iov = (struct iovec *)iov,
530 .msg_iovlen = count,
532 ssize_t ret;
534 /* Next time, write directly. Do not retry to connect. */
535 tls->writev = vlc_tls_SocketWrite;
537 ret = sendmsg(vlc_tls_SocketGetFD(tls), &msg, MSG_NOSIGNAL|MSG_FASTOPEN);
538 if (ret >= 0)
539 { /* Fast open in progress */
540 return ret;
543 if (errno == EINPROGRESS)
545 if (vlc_tls_WaitConnect(tls))
546 return -1;
548 else
549 if (errno != EOPNOTSUPP)
550 return -1;
551 /* Fast open not supported or disabled... fallback to normal mode */
552 #else
553 tls->writev = vlc_tls_SocketWrite;
554 #endif
556 if (vlc_tls_Connect(tls))
557 return -1;
559 return vlc_tls_SocketWrite(tls, iov, count);
562 vlc_tls_t *vlc_tls_SocketOpenAddrInfo(const struct addrinfo *restrict info,
563 bool defer_connect)
565 vlc_tls_t *sock = vlc_tls_SocketAddrInfo(info);
566 if (sock == NULL)
567 return NULL;
569 if (defer_connect)
570 { /* The socket is not connected yet.
571 * The connection will be triggered on the first send. */
572 sock->writev = vlc_tls_ConnectWrite;
574 else
576 if (vlc_tls_Connect(sock))
578 vlc_tls_SessionDelete(sock);
579 sock = NULL;
582 return sock;
585 vlc_tls_t *vlc_tls_SocketOpenTCP(vlc_object_t *obj, const char *name,
586 unsigned port)
588 struct addrinfo hints =
590 .ai_socktype = SOCK_STREAM,
591 .ai_protocol = IPPROTO_TCP,
592 }, *res;
594 assert(name != NULL);
595 msg_Dbg(obj, "resolving %s ...", name);
597 int val = vlc_getaddrinfo_i11e(name, port, &hints, &res);
598 if (val != 0)
599 { /* TODO: C locale for gai_strerror() */
600 msg_Err(obj, "cannot resolve %s port %u: %s", name, port,
601 gai_strerror(val));
602 return NULL;
605 msg_Dbg(obj, "connecting to %s port %u ...", name, port);
607 /* TODO: implement RFC6555 */
608 for (const struct addrinfo *p = res; p != NULL; p = p->ai_next)
610 vlc_tls_t *tls = vlc_tls_SocketOpenAddrInfo(p, false);
611 if (tls == NULL)
613 msg_Err(obj, "connection error: %s", vlc_strerror_c(errno));
614 continue;
617 freeaddrinfo(res);
618 return tls;
621 freeaddrinfo(res);
622 return NULL;
625 vlc_tls_t *vlc_tls_SocketOpenTLS(vlc_tls_creds_t *creds, const char *name,
626 unsigned port, const char *service,
627 const char *const *alpn, char **alp)
629 struct addrinfo hints =
631 .ai_socktype = SOCK_STREAM,
632 .ai_protocol = IPPROTO_TCP,
633 }, *res;
635 msg_Dbg(creds, "resolving %s ...", name);
637 int val = vlc_getaddrinfo_i11e(name, port, &hints, &res);
638 if (val != 0)
639 { /* TODO: C locale for gai_strerror() */
640 msg_Err(creds, "cannot resolve %s port %u: %s", name, port,
641 gai_strerror(val));
642 return NULL;
645 for (const struct addrinfo *p = res; p != NULL; p = p->ai_next)
647 vlc_tls_t *tcp = vlc_tls_SocketOpenAddrInfo(p, true);
648 if (tcp == NULL)
650 msg_Err(creds, "socket error: %s", vlc_strerror_c(errno));
651 continue;
654 vlc_tls_t *tls = vlc_tls_ClientSessionCreate(creds, tcp, name, service,
655 alpn, alp);
656 if (tls != NULL)
657 { /* Success! */
658 freeaddrinfo(res);
659 return tls;
662 msg_Err(creds, "connection error: %s", vlc_strerror_c(errno));
663 vlc_tls_SessionDelete(tcp);
666 /* Failure! */
667 freeaddrinfo(res);
668 return NULL;