1aa772005SRobert Watson /*-
2aa772005SRobert Watson * Copyright (c) 2011 The FreeBSD Foundation
3aa772005SRobert Watson * All rights reserved.
4aa772005SRobert Watson *
5aa772005SRobert Watson * This software was developed by Pawel Jakub Dawidek under sponsorship from
6aa772005SRobert Watson * the FreeBSD Foundation.
7aa772005SRobert Watson *
8aa772005SRobert Watson * Redistribution and use in source and binary forms, with or without
9aa772005SRobert Watson * modification, are permitted provided that the following conditions
10aa772005SRobert Watson * are met:
11aa772005SRobert Watson * 1. Redistributions of source code must retain the above copyright
12aa772005SRobert Watson * notice, this list of conditions and the following disclaimer.
13aa772005SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright
14aa772005SRobert Watson * notice, this list of conditions and the following disclaimer in the
15aa772005SRobert Watson * documentation and/or other materials provided with the distribution.
16aa772005SRobert Watson *
17aa772005SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18aa772005SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19aa772005SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20aa772005SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21aa772005SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22aa772005SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23aa772005SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24aa772005SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25aa772005SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26aa772005SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27aa772005SRobert Watson * SUCH DAMAGE.
28aa772005SRobert Watson */
29aa772005SRobert Watson
30aa772005SRobert Watson #include <config/config.h>
31aa772005SRobert Watson
32aa772005SRobert Watson #include <sys/param.h> /* MAXHOSTNAMELEN */
33aa772005SRobert Watson #include <sys/socket.h>
34aa772005SRobert Watson
35aa772005SRobert Watson #include <arpa/inet.h>
36aa772005SRobert Watson
37aa772005SRobert Watson #include <netinet/in.h>
38aa772005SRobert Watson #include <netinet/tcp.h>
39aa772005SRobert Watson
40aa772005SRobert Watson #include <errno.h>
41aa772005SRobert Watson #include <fcntl.h>
42aa772005SRobert Watson #include <netdb.h>
43aa772005SRobert Watson #include <signal.h>
44aa772005SRobert Watson #include <stdbool.h>
45aa772005SRobert Watson #include <stdint.h>
46aa772005SRobert Watson #include <stdio.h>
47aa772005SRobert Watson #include <string.h>
48aa772005SRobert Watson #include <unistd.h>
49aa772005SRobert Watson
50aa772005SRobert Watson #include <openssl/err.h>
51aa772005SRobert Watson #include <openssl/ssl.h>
52aa772005SRobert Watson
53aa772005SRobert Watson #include <compat/compat.h>
54aa772005SRobert Watson #ifndef HAVE_CLOSEFROM
55aa772005SRobert Watson #include <compat/closefrom.h>
56aa772005SRobert Watson #endif
57aa772005SRobert Watson #ifndef HAVE_STRLCPY
58aa772005SRobert Watson #include <compat/strlcpy.h>
59aa772005SRobert Watson #endif
60aa772005SRobert Watson
61aa772005SRobert Watson #include "pjdlog.h"
62aa772005SRobert Watson #include "proto_impl.h"
63aa772005SRobert Watson #include "sandbox.h"
64aa772005SRobert Watson #include "subr.h"
65aa772005SRobert Watson
66aa772005SRobert Watson #define TLS_CTX_MAGIC 0x715c7
67aa772005SRobert Watson struct tls_ctx {
68aa772005SRobert Watson int tls_magic;
69aa772005SRobert Watson struct proto_conn *tls_sock;
70aa772005SRobert Watson struct proto_conn *tls_tcp;
71aa772005SRobert Watson char tls_laddr[256];
72aa772005SRobert Watson char tls_raddr[256];
73aa772005SRobert Watson int tls_side;
74aa772005SRobert Watson #define TLS_SIDE_CLIENT 0
75aa772005SRobert Watson #define TLS_SIDE_SERVER_LISTEN 1
76aa772005SRobert Watson #define TLS_SIDE_SERVER_WORK 2
77aa772005SRobert Watson bool tls_wait_called;
78aa772005SRobert Watson };
79aa772005SRobert Watson
80aa772005SRobert Watson #define TLS_DEFAULT_TIMEOUT 30
81aa772005SRobert Watson
82aa772005SRobert Watson static int tls_connect_wait(void *ctx, int timeout);
83aa772005SRobert Watson static void tls_close(void *ctx);
84aa772005SRobert Watson
85aa772005SRobert Watson static void
block(int fd)86aa772005SRobert Watson block(int fd)
87aa772005SRobert Watson {
88aa772005SRobert Watson int flags;
89aa772005SRobert Watson
90aa772005SRobert Watson flags = fcntl(fd, F_GETFL);
91aa772005SRobert Watson if (flags == -1)
92aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "fcntl(F_GETFL) failed");
93aa772005SRobert Watson flags &= ~O_NONBLOCK;
94aa772005SRobert Watson if (fcntl(fd, F_SETFL, flags) == -1)
95aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "fcntl(F_SETFL) failed");
96aa772005SRobert Watson }
97aa772005SRobert Watson
98aa772005SRobert Watson static void
nonblock(int fd)99aa772005SRobert Watson nonblock(int fd)
100aa772005SRobert Watson {
101aa772005SRobert Watson int flags;
102aa772005SRobert Watson
103aa772005SRobert Watson flags = fcntl(fd, F_GETFL);
104aa772005SRobert Watson if (flags == -1)
105aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "fcntl(F_GETFL) failed");
106aa772005SRobert Watson flags |= O_NONBLOCK;
107aa772005SRobert Watson if (fcntl(fd, F_SETFL, flags) == -1)
108aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "fcntl(F_SETFL) failed");
109aa772005SRobert Watson }
110aa772005SRobert Watson
111aa772005SRobert Watson static int
wait_for_fd(int fd,int timeout)112aa772005SRobert Watson wait_for_fd(int fd, int timeout)
113aa772005SRobert Watson {
114aa772005SRobert Watson struct timeval tv;
115aa772005SRobert Watson fd_set fdset;
116aa772005SRobert Watson int error, ret;
117aa772005SRobert Watson
118aa772005SRobert Watson error = 0;
119aa772005SRobert Watson
120aa772005SRobert Watson for (;;) {
121aa772005SRobert Watson FD_ZERO(&fdset);
122aa772005SRobert Watson FD_SET(fd, &fdset);
123aa772005SRobert Watson
124aa772005SRobert Watson tv.tv_sec = timeout;
125aa772005SRobert Watson tv.tv_usec = 0;
126aa772005SRobert Watson
127aa772005SRobert Watson ret = select(fd + 1, NULL, &fdset, NULL,
128aa772005SRobert Watson timeout == -1 ? NULL : &tv);
129aa772005SRobert Watson if (ret == 0) {
130aa772005SRobert Watson error = ETIMEDOUT;
131aa772005SRobert Watson break;
132aa772005SRobert Watson } else if (ret == -1) {
133aa772005SRobert Watson if (errno == EINTR)
134aa772005SRobert Watson continue;
135aa772005SRobert Watson error = errno;
136aa772005SRobert Watson break;
137aa772005SRobert Watson }
138aa772005SRobert Watson PJDLOG_ASSERT(ret > 0);
139aa772005SRobert Watson PJDLOG_ASSERT(FD_ISSET(fd, &fdset));
140aa772005SRobert Watson break;
141aa772005SRobert Watson }
142aa772005SRobert Watson
143aa772005SRobert Watson return (error);
144aa772005SRobert Watson }
145aa772005SRobert Watson
146aa772005SRobert Watson static void
ssl_log_errors(void)147aa772005SRobert Watson ssl_log_errors(void)
148aa772005SRobert Watson {
149aa772005SRobert Watson unsigned long error;
150aa772005SRobert Watson
151aa772005SRobert Watson while ((error = ERR_get_error()) != 0)
152aa772005SRobert Watson pjdlog_error("SSL error: %s", ERR_error_string(error, NULL));
153aa772005SRobert Watson }
154aa772005SRobert Watson
155aa772005SRobert Watson static int
ssl_check_error(SSL * ssl,int ret)156aa772005SRobert Watson ssl_check_error(SSL *ssl, int ret)
157aa772005SRobert Watson {
158aa772005SRobert Watson int error;
159aa772005SRobert Watson
160aa772005SRobert Watson error = SSL_get_error(ssl, ret);
161aa772005SRobert Watson
162aa772005SRobert Watson switch (error) {
163aa772005SRobert Watson case SSL_ERROR_NONE:
164aa772005SRobert Watson return (0);
165aa772005SRobert Watson case SSL_ERROR_WANT_READ:
166aa772005SRobert Watson pjdlog_debug(2, "SSL_ERROR_WANT_READ");
167aa772005SRobert Watson return (-1);
168aa772005SRobert Watson case SSL_ERROR_WANT_WRITE:
169aa772005SRobert Watson pjdlog_debug(2, "SSL_ERROR_WANT_WRITE");
170aa772005SRobert Watson return (-1);
171aa772005SRobert Watson case SSL_ERROR_ZERO_RETURN:
172aa772005SRobert Watson pjdlog_exitx(EX_OK, "Connection closed.");
173aa772005SRobert Watson case SSL_ERROR_SYSCALL:
174aa772005SRobert Watson ssl_log_errors();
175aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "SSL I/O error.");
176aa772005SRobert Watson case SSL_ERROR_SSL:
177aa772005SRobert Watson ssl_log_errors();
178aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "SSL protocol error.");
179aa772005SRobert Watson default:
180aa772005SRobert Watson ssl_log_errors();
181aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "Unknown SSL error (%d).", error);
182aa772005SRobert Watson }
183aa772005SRobert Watson }
184aa772005SRobert Watson
185aa772005SRobert Watson static void
tcp_recv_ssl_send(int recvfd,SSL * sendssl)186aa772005SRobert Watson tcp_recv_ssl_send(int recvfd, SSL *sendssl)
187aa772005SRobert Watson {
188aa772005SRobert Watson static unsigned char buf[65536];
189aa772005SRobert Watson ssize_t tcpdone;
190aa772005SRobert Watson int sendfd, ssldone;
191aa772005SRobert Watson
192aa772005SRobert Watson sendfd = SSL_get_fd(sendssl);
193aa772005SRobert Watson PJDLOG_ASSERT(sendfd >= 0);
194aa772005SRobert Watson pjdlog_debug(2, "%s: start %d -> %d", __func__, recvfd, sendfd);
195aa772005SRobert Watson for (;;) {
196aa772005SRobert Watson tcpdone = recv(recvfd, buf, sizeof(buf), 0);
197aa772005SRobert Watson pjdlog_debug(2, "%s: recv() returned %zd", __func__, tcpdone);
198aa772005SRobert Watson if (tcpdone == 0) {
199aa772005SRobert Watson pjdlog_debug(1, "Connection terminated.");
200aa772005SRobert Watson exit(0);
201aa772005SRobert Watson } else if (tcpdone == -1) {
202aa772005SRobert Watson if (errno == EINTR)
203aa772005SRobert Watson continue;
204aa772005SRobert Watson else if (errno == EAGAIN)
205aa772005SRobert Watson break;
206aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "recv() failed");
207aa772005SRobert Watson }
208aa772005SRobert Watson for (;;) {
209aa772005SRobert Watson ssldone = SSL_write(sendssl, buf, (int)tcpdone);
210aa772005SRobert Watson pjdlog_debug(2, "%s: send() returned %d", __func__,
211aa772005SRobert Watson ssldone);
212aa772005SRobert Watson if (ssl_check_error(sendssl, ssldone) == -1) {
213aa772005SRobert Watson (void)wait_for_fd(sendfd, -1);
214aa772005SRobert Watson continue;
215aa772005SRobert Watson }
216aa772005SRobert Watson PJDLOG_ASSERT(ssldone == tcpdone);
217aa772005SRobert Watson break;
218aa772005SRobert Watson }
219aa772005SRobert Watson }
220aa772005SRobert Watson pjdlog_debug(2, "%s: done %d -> %d", __func__, recvfd, sendfd);
221aa772005SRobert Watson }
222aa772005SRobert Watson
223aa772005SRobert Watson static void
ssl_recv_tcp_send(SSL * recvssl,int sendfd)224aa772005SRobert Watson ssl_recv_tcp_send(SSL *recvssl, int sendfd)
225aa772005SRobert Watson {
226aa772005SRobert Watson static unsigned char buf[65536];
227aa772005SRobert Watson unsigned char *ptr;
228aa772005SRobert Watson ssize_t tcpdone;
229aa772005SRobert Watson size_t todo;
230aa772005SRobert Watson int recvfd, ssldone;
231aa772005SRobert Watson
232aa772005SRobert Watson recvfd = SSL_get_fd(recvssl);
233aa772005SRobert Watson PJDLOG_ASSERT(recvfd >= 0);
234aa772005SRobert Watson pjdlog_debug(2, "%s: start %d -> %d", __func__, recvfd, sendfd);
235aa772005SRobert Watson for (;;) {
236aa772005SRobert Watson ssldone = SSL_read(recvssl, buf, sizeof(buf));
237aa772005SRobert Watson pjdlog_debug(2, "%s: SSL_read() returned %d", __func__,
238aa772005SRobert Watson ssldone);
239aa772005SRobert Watson if (ssl_check_error(recvssl, ssldone) == -1)
240aa772005SRobert Watson break;
241aa772005SRobert Watson todo = (size_t)ssldone;
242aa772005SRobert Watson ptr = buf;
243aa772005SRobert Watson do {
244aa772005SRobert Watson tcpdone = send(sendfd, ptr, todo, MSG_NOSIGNAL);
245aa772005SRobert Watson pjdlog_debug(2, "%s: send() returned %zd", __func__,
246aa772005SRobert Watson tcpdone);
247aa772005SRobert Watson if (tcpdone == 0) {
248aa772005SRobert Watson pjdlog_debug(1, "Connection terminated.");
249aa772005SRobert Watson exit(0);
250aa772005SRobert Watson } else if (tcpdone == -1) {
251aa772005SRobert Watson if (errno == EINTR || errno == ENOBUFS)
252aa772005SRobert Watson continue;
253aa772005SRobert Watson if (errno == EAGAIN) {
254aa772005SRobert Watson (void)wait_for_fd(sendfd, -1);
255aa772005SRobert Watson continue;
256aa772005SRobert Watson }
257aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "send() failed");
258aa772005SRobert Watson }
259aa772005SRobert Watson todo -= tcpdone;
260aa772005SRobert Watson ptr += tcpdone;
261aa772005SRobert Watson } while (todo > 0);
262aa772005SRobert Watson }
263aa772005SRobert Watson pjdlog_debug(2, "%s: done %d -> %d", __func__, recvfd, sendfd);
264aa772005SRobert Watson }
265aa772005SRobert Watson
266aa772005SRobert Watson static void
tls_loop(int sockfd,SSL * tcpssl)267aa772005SRobert Watson tls_loop(int sockfd, SSL *tcpssl)
268aa772005SRobert Watson {
269aa772005SRobert Watson fd_set fds;
270aa772005SRobert Watson int maxfd, tcpfd;
271aa772005SRobert Watson
272aa772005SRobert Watson tcpfd = SSL_get_fd(tcpssl);
273aa772005SRobert Watson PJDLOG_ASSERT(tcpfd >= 0);
274aa772005SRobert Watson
275aa772005SRobert Watson for (;;) {
276aa772005SRobert Watson FD_ZERO(&fds);
277aa772005SRobert Watson FD_SET(sockfd, &fds);
278aa772005SRobert Watson FD_SET(tcpfd, &fds);
279aa772005SRobert Watson maxfd = MAX(sockfd, tcpfd);
280aa772005SRobert Watson
281aa772005SRobert Watson PJDLOG_ASSERT(maxfd + 1 <= (int)FD_SETSIZE);
282aa772005SRobert Watson if (select(maxfd + 1, &fds, NULL, NULL, NULL) == -1) {
283aa772005SRobert Watson if (errno == EINTR)
284aa772005SRobert Watson continue;
285aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "select() failed");
286aa772005SRobert Watson }
287aa772005SRobert Watson if (FD_ISSET(sockfd, &fds))
288aa772005SRobert Watson tcp_recv_ssl_send(sockfd, tcpssl);
289aa772005SRobert Watson if (FD_ISSET(tcpfd, &fds))
290aa772005SRobert Watson ssl_recv_tcp_send(tcpssl, sockfd);
291aa772005SRobert Watson }
292aa772005SRobert Watson }
293aa772005SRobert Watson
294aa772005SRobert Watson static void
tls_certificate_verify(SSL * ssl,const char * fingerprint)295aa772005SRobert Watson tls_certificate_verify(SSL *ssl, const char *fingerprint)
296aa772005SRobert Watson {
297aa772005SRobert Watson unsigned char md[EVP_MAX_MD_SIZE];
298aa772005SRobert Watson char mdstr[sizeof("SHA256=") - 1 + EVP_MAX_MD_SIZE * 3];
299aa772005SRobert Watson char *mdstrp;
300aa772005SRobert Watson unsigned int i, mdsize;
301aa772005SRobert Watson X509 *cert;
302aa772005SRobert Watson
303aa772005SRobert Watson if (fingerprint[0] == '\0') {
304aa772005SRobert Watson pjdlog_debug(1, "No fingerprint verification requested.");
305aa772005SRobert Watson return;
306aa772005SRobert Watson }
307aa772005SRobert Watson
308aa772005SRobert Watson cert = SSL_get_peer_certificate(ssl);
309aa772005SRobert Watson if (cert == NULL)
310aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "No peer certificate received.");
311aa772005SRobert Watson
312aa772005SRobert Watson if (X509_digest(cert, EVP_sha256(), md, &mdsize) != 1)
313aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "X509_digest() failed.");
314aa772005SRobert Watson PJDLOG_ASSERT(mdsize <= EVP_MAX_MD_SIZE);
315aa772005SRobert Watson
316aa772005SRobert Watson X509_free(cert);
317aa772005SRobert Watson
318aa772005SRobert Watson (void)strlcpy(mdstr, "SHA256=", sizeof(mdstr));
319aa772005SRobert Watson mdstrp = mdstr + strlen(mdstr);
320aa772005SRobert Watson for (i = 0; i < mdsize; i++) {
321aa772005SRobert Watson PJDLOG_VERIFY(mdstrp + 3 <= mdstr + sizeof(mdstr));
322aa772005SRobert Watson (void)sprintf(mdstrp, "%02hhX:", md[i]);
323aa772005SRobert Watson mdstrp += 3;
324aa772005SRobert Watson }
325aa772005SRobert Watson /* Clear last colon. */
326aa772005SRobert Watson mdstrp[-1] = '\0';
327aa772005SRobert Watson if (strcasecmp(mdstr, fingerprint) != 0) {
328aa772005SRobert Watson pjdlog_exitx(EX_NOPERM,
329aa772005SRobert Watson "Finger print doesn't match. Received \"%s\", expected \"%s\"",
330aa772005SRobert Watson mdstr, fingerprint);
331aa772005SRobert Watson }
332aa772005SRobert Watson }
333aa772005SRobert Watson
334aa772005SRobert Watson static void
tls_exec_client(const char * user,int startfd,const char * srcaddr,const char * dstaddr,const char * fingerprint,const char * defport,int timeout,int debuglevel)335aa772005SRobert Watson tls_exec_client(const char *user, int startfd, const char *srcaddr,
336aa772005SRobert Watson const char *dstaddr, const char *fingerprint, const char *defport,
337aa772005SRobert Watson int timeout, int debuglevel)
338aa772005SRobert Watson {
339aa772005SRobert Watson struct proto_conn *tcp;
340aa772005SRobert Watson char *saddr, *daddr;
341aa772005SRobert Watson SSL_CTX *sslctx;
342aa772005SRobert Watson SSL *ssl;
343aa772005SRobert Watson long ret;
344aa772005SRobert Watson int sockfd, tcpfd;
345aa772005SRobert Watson uint8_t connected;
346aa772005SRobert Watson
347aa772005SRobert Watson pjdlog_debug_set(debuglevel);
348aa772005SRobert Watson pjdlog_prefix_set("[TLS sandbox] (client) ");
349aa772005SRobert Watson #ifdef HAVE_SETPROCTITLE
350aa772005SRobert Watson setproctitle("[TLS sandbox] (client) ");
351aa772005SRobert Watson #endif
352aa772005SRobert Watson proto_set("tcp:port", defport);
353aa772005SRobert Watson
354aa772005SRobert Watson sockfd = startfd;
355aa772005SRobert Watson
356aa772005SRobert Watson /* Change tls:// to tcp://. */
357aa772005SRobert Watson if (srcaddr == NULL) {
358aa772005SRobert Watson saddr = NULL;
359aa772005SRobert Watson } else {
360aa772005SRobert Watson saddr = strdup(srcaddr);
361aa772005SRobert Watson if (saddr == NULL)
362aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "Unable to allocate memory.");
363aa772005SRobert Watson bcopy("tcp://", saddr, 6);
364aa772005SRobert Watson }
365aa772005SRobert Watson daddr = strdup(dstaddr);
366aa772005SRobert Watson if (daddr == NULL)
367aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "Unable to allocate memory.");
368aa772005SRobert Watson bcopy("tcp://", daddr, 6);
369aa772005SRobert Watson
370aa772005SRobert Watson /* Establish TCP connection. */
371aa772005SRobert Watson if (proto_connect(saddr, daddr, timeout, &tcp) == -1)
372aa772005SRobert Watson exit(EX_TEMPFAIL);
373aa772005SRobert Watson
374*b01c10d2SEd Maste #if OPENSSL_VERSION_NUMBER < 0x10100000L
375aa772005SRobert Watson SSL_load_error_strings();
376aa772005SRobert Watson SSL_library_init();
377*b01c10d2SEd Maste #endif
378aa772005SRobert Watson
379aa772005SRobert Watson /*
380aa772005SRobert Watson * TODO: On FreeBSD we could move this below sandbox() once libc and
381aa772005SRobert Watson * libcrypto use sysctl kern.arandom to obtain random data
382aa772005SRobert Watson * instead of /dev/urandom and friends.
383aa772005SRobert Watson */
3844a189b08SJung-uk Kim sslctx = SSL_CTX_new(TLS_client_method());
385aa772005SRobert Watson if (sslctx == NULL)
386aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "SSL_CTX_new() failed.");
387aa772005SRobert Watson
388aa772005SRobert Watson if (sandbox(user, true, "proto_tls client: %s", dstaddr) != 0)
389aa772005SRobert Watson pjdlog_exitx(EX_CONFIG, "Unable to sandbox TLS client.");
390aa772005SRobert Watson pjdlog_debug(1, "Privileges successfully dropped.");
391aa772005SRobert Watson
392aa772005SRobert Watson SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
393aa772005SRobert Watson
394aa772005SRobert Watson /* Load CA certs. */
395aa772005SRobert Watson /* TODO */
396aa772005SRobert Watson //SSL_CTX_load_verify_locations(sslctx, cacerts_file, NULL);
397aa772005SRobert Watson
398aa772005SRobert Watson ssl = SSL_new(sslctx);
399aa772005SRobert Watson if (ssl == NULL)
400aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "SSL_new() failed.");
401aa772005SRobert Watson
402aa772005SRobert Watson tcpfd = proto_descriptor(tcp);
403aa772005SRobert Watson
404aa772005SRobert Watson block(tcpfd);
405aa772005SRobert Watson
406aa772005SRobert Watson if (SSL_set_fd(ssl, tcpfd) != 1)
407aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "SSL_set_fd() failed.");
408aa772005SRobert Watson
409aa772005SRobert Watson ret = SSL_connect(ssl);
410aa772005SRobert Watson ssl_check_error(ssl, (int)ret);
411aa772005SRobert Watson
412aa772005SRobert Watson nonblock(sockfd);
413aa772005SRobert Watson nonblock(tcpfd);
414aa772005SRobert Watson
415aa772005SRobert Watson tls_certificate_verify(ssl, fingerprint);
416aa772005SRobert Watson
417aa772005SRobert Watson /*
4183008333dSChristian S.J. Peron * The following byte is sent to make proto_connect_wait() work.
419aa772005SRobert Watson */
420aa772005SRobert Watson connected = 1;
421aa772005SRobert Watson for (;;) {
422aa772005SRobert Watson switch (send(sockfd, &connected, sizeof(connected), 0)) {
423aa772005SRobert Watson case -1:
424aa772005SRobert Watson if (errno == EINTR || errno == ENOBUFS)
425aa772005SRobert Watson continue;
426aa772005SRobert Watson if (errno == EAGAIN) {
427aa772005SRobert Watson (void)wait_for_fd(sockfd, -1);
428aa772005SRobert Watson continue;
429aa772005SRobert Watson }
430aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "send() failed");
431aa772005SRobert Watson case 0:
432aa772005SRobert Watson pjdlog_debug(1, "Connection terminated.");
433aa772005SRobert Watson exit(0);
434aa772005SRobert Watson case 1:
435aa772005SRobert Watson break;
436aa772005SRobert Watson }
437aa772005SRobert Watson break;
438aa772005SRobert Watson }
439aa772005SRobert Watson
440aa772005SRobert Watson tls_loop(sockfd, ssl);
441aa772005SRobert Watson }
442aa772005SRobert Watson
443aa772005SRobert Watson static void
tls_call_exec_client(struct proto_conn * sock,const char * srcaddr,const char * dstaddr,int timeout)444aa772005SRobert Watson tls_call_exec_client(struct proto_conn *sock, const char *srcaddr,
445aa772005SRobert Watson const char *dstaddr, int timeout)
446aa772005SRobert Watson {
447aa772005SRobert Watson char *timeoutstr, *startfdstr, *debugstr;
448aa772005SRobert Watson int startfd;
449aa772005SRobert Watson
450aa772005SRobert Watson /* Declare that we are receiver. */
451aa772005SRobert Watson proto_recv(sock, NULL, 0);
452aa772005SRobert Watson
453aa772005SRobert Watson if (pjdlog_mode_get() == PJDLOG_MODE_STD)
454aa772005SRobert Watson startfd = 3;
455aa772005SRobert Watson else /* if (pjdlog_mode_get() == PJDLOG_MODE_SYSLOG) */
456aa772005SRobert Watson startfd = 0;
457aa772005SRobert Watson
458aa772005SRobert Watson if (proto_descriptor(sock) != startfd) {
459aa772005SRobert Watson /* Move socketpair descriptor to descriptor number startfd. */
460aa772005SRobert Watson if (dup2(proto_descriptor(sock), startfd) == -1)
461aa772005SRobert Watson pjdlog_exit(EX_OSERR, "dup2() failed");
462aa772005SRobert Watson proto_close(sock);
463aa772005SRobert Watson } else {
464aa772005SRobert Watson /*
4653008333dSChristian S.J. Peron * The FD_CLOEXEC is cleared by dup2(2), so when we do not
466aa772005SRobert Watson * call it, we have to clear it by hand in case it is set.
467aa772005SRobert Watson */
468aa772005SRobert Watson if (fcntl(startfd, F_SETFD, 0) == -1)
469aa772005SRobert Watson pjdlog_exit(EX_OSERR, "fcntl() failed");
470aa772005SRobert Watson }
471aa772005SRobert Watson
472aa772005SRobert Watson closefrom(startfd + 1);
473aa772005SRobert Watson
474aa772005SRobert Watson if (asprintf(&startfdstr, "%d", startfd) == -1)
475aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "asprintf() failed");
476aa772005SRobert Watson if (timeout == -1)
477aa772005SRobert Watson timeout = TLS_DEFAULT_TIMEOUT;
478aa772005SRobert Watson if (asprintf(&timeoutstr, "%d", timeout) == -1)
479aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "asprintf() failed");
480aa772005SRobert Watson if (asprintf(&debugstr, "%d", pjdlog_debug_get()) == -1)
481aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "asprintf() failed");
482aa772005SRobert Watson
483aa772005SRobert Watson execl(proto_get("execpath"), proto_get("execpath"), "proto", "tls",
484aa772005SRobert Watson proto_get("user"), "client", startfdstr,
485aa772005SRobert Watson srcaddr == NULL ? "" : srcaddr, dstaddr,
486aa772005SRobert Watson proto_get("tls:fingerprint"), proto_get("tcp:port"), timeoutstr,
487aa772005SRobert Watson debugstr, NULL);
488aa772005SRobert Watson pjdlog_exit(EX_SOFTWARE, "execl() failed");
489aa772005SRobert Watson }
490aa772005SRobert Watson
491aa772005SRobert Watson static int
tls_connect(const char * srcaddr,const char * dstaddr,int timeout,void ** ctxp)492aa772005SRobert Watson tls_connect(const char *srcaddr, const char *dstaddr, int timeout, void **ctxp)
493aa772005SRobert Watson {
494aa772005SRobert Watson struct tls_ctx *tlsctx;
495aa772005SRobert Watson struct proto_conn *sock;
496aa772005SRobert Watson pid_t pid;
497aa772005SRobert Watson int error;
498aa772005SRobert Watson
499aa772005SRobert Watson PJDLOG_ASSERT(srcaddr == NULL || srcaddr[0] != '\0');
500aa772005SRobert Watson PJDLOG_ASSERT(dstaddr != NULL);
501aa772005SRobert Watson PJDLOG_ASSERT(timeout >= -1);
502aa772005SRobert Watson PJDLOG_ASSERT(ctxp != NULL);
503aa772005SRobert Watson
504aa772005SRobert Watson if (strncmp(dstaddr, "tls://", 6) != 0)
505aa772005SRobert Watson return (-1);
506aa772005SRobert Watson if (srcaddr != NULL && strncmp(srcaddr, "tls://", 6) != 0)
507aa772005SRobert Watson return (-1);
508aa772005SRobert Watson
509aa772005SRobert Watson if (proto_connect(NULL, "socketpair://", -1, &sock) == -1)
510aa772005SRobert Watson return (errno);
511aa772005SRobert Watson
512aa772005SRobert Watson #if 0
513aa772005SRobert Watson /*
514aa772005SRobert Watson * We use rfork() with the following flags to disable SIGCHLD
515aa772005SRobert Watson * delivery upon the sandbox process exit.
516aa772005SRobert Watson */
517aa772005SRobert Watson pid = rfork(RFFDG | RFPROC | RFTSIGZMB | RFTSIGFLAGS(0));
518aa772005SRobert Watson #else
519aa772005SRobert Watson /*
520aa772005SRobert Watson * We don't use rfork() to be able to log information about sandbox
521aa772005SRobert Watson * process exiting.
522aa772005SRobert Watson */
523aa772005SRobert Watson pid = fork();
524aa772005SRobert Watson #endif
525aa772005SRobert Watson switch (pid) {
526aa772005SRobert Watson case -1:
527aa772005SRobert Watson /* Failure. */
528aa772005SRobert Watson error = errno;
529aa772005SRobert Watson proto_close(sock);
530aa772005SRobert Watson return (error);
531aa772005SRobert Watson case 0:
532aa772005SRobert Watson /* Child. */
533aa772005SRobert Watson pjdlog_prefix_set("[TLS sandbox] (client) ");
534aa772005SRobert Watson #ifdef HAVE_SETPROCTITLE
535aa772005SRobert Watson setproctitle("[TLS sandbox] (client) ");
536aa772005SRobert Watson #endif
537aa772005SRobert Watson tls_call_exec_client(sock, srcaddr, dstaddr, timeout);
538aa772005SRobert Watson /* NOTREACHED */
539aa772005SRobert Watson default:
540aa772005SRobert Watson /* Parent. */
541aa772005SRobert Watson tlsctx = calloc(1, sizeof(*tlsctx));
542aa772005SRobert Watson if (tlsctx == NULL) {
543aa772005SRobert Watson error = errno;
544aa772005SRobert Watson proto_close(sock);
545aa772005SRobert Watson (void)kill(pid, SIGKILL);
546aa772005SRobert Watson return (error);
547aa772005SRobert Watson }
548aa772005SRobert Watson proto_send(sock, NULL, 0);
549aa772005SRobert Watson tlsctx->tls_sock = sock;
550aa772005SRobert Watson tlsctx->tls_tcp = NULL;
551aa772005SRobert Watson tlsctx->tls_side = TLS_SIDE_CLIENT;
552aa772005SRobert Watson tlsctx->tls_wait_called = false;
553aa772005SRobert Watson tlsctx->tls_magic = TLS_CTX_MAGIC;
554aa772005SRobert Watson if (timeout >= 0) {
555aa772005SRobert Watson error = tls_connect_wait(tlsctx, timeout);
556aa772005SRobert Watson if (error != 0) {
557aa772005SRobert Watson (void)kill(pid, SIGKILL);
558aa772005SRobert Watson tls_close(tlsctx);
559aa772005SRobert Watson return (error);
560aa772005SRobert Watson }
561aa772005SRobert Watson }
562aa772005SRobert Watson *ctxp = tlsctx;
563aa772005SRobert Watson return (0);
564aa772005SRobert Watson }
565aa772005SRobert Watson }
566aa772005SRobert Watson
567aa772005SRobert Watson static int
tls_connect_wait(void * ctx,int timeout)568aa772005SRobert Watson tls_connect_wait(void *ctx, int timeout)
569aa772005SRobert Watson {
570aa772005SRobert Watson struct tls_ctx *tlsctx = ctx;
571aa772005SRobert Watson int error, sockfd;
572aa772005SRobert Watson uint8_t connected;
573aa772005SRobert Watson
574aa772005SRobert Watson PJDLOG_ASSERT(tlsctx != NULL);
575aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
576aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_side == TLS_SIDE_CLIENT);
577aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
578aa772005SRobert Watson PJDLOG_ASSERT(!tlsctx->tls_wait_called);
579aa772005SRobert Watson PJDLOG_ASSERT(timeout >= 0);
580aa772005SRobert Watson
581aa772005SRobert Watson sockfd = proto_descriptor(tlsctx->tls_sock);
582aa772005SRobert Watson error = wait_for_fd(sockfd, timeout);
583aa772005SRobert Watson if (error != 0)
584aa772005SRobert Watson return (error);
585aa772005SRobert Watson
586aa772005SRobert Watson for (;;) {
587aa772005SRobert Watson switch (recv(sockfd, &connected, sizeof(connected),
588aa772005SRobert Watson MSG_WAITALL)) {
589aa772005SRobert Watson case -1:
590aa772005SRobert Watson if (errno == EINTR || errno == ENOBUFS)
591aa772005SRobert Watson continue;
592aa772005SRobert Watson error = errno;
593aa772005SRobert Watson break;
594aa772005SRobert Watson case 0:
595aa772005SRobert Watson pjdlog_debug(1, "Connection terminated.");
596aa772005SRobert Watson error = ENOTCONN;
597aa772005SRobert Watson break;
598aa772005SRobert Watson case 1:
599aa772005SRobert Watson tlsctx->tls_wait_called = true;
600aa772005SRobert Watson break;
601aa772005SRobert Watson }
602aa772005SRobert Watson break;
603aa772005SRobert Watson }
604aa772005SRobert Watson
605aa772005SRobert Watson return (error);
606aa772005SRobert Watson }
607aa772005SRobert Watson
608aa772005SRobert Watson static int
tls_server(const char * lstaddr,void ** ctxp)609aa772005SRobert Watson tls_server(const char *lstaddr, void **ctxp)
610aa772005SRobert Watson {
611aa772005SRobert Watson struct proto_conn *tcp;
612aa772005SRobert Watson struct tls_ctx *tlsctx;
613aa772005SRobert Watson char *laddr;
614aa772005SRobert Watson int error;
615aa772005SRobert Watson
616aa772005SRobert Watson if (strncmp(lstaddr, "tls://", 6) != 0)
617aa772005SRobert Watson return (-1);
618aa772005SRobert Watson
619aa772005SRobert Watson tlsctx = malloc(sizeof(*tlsctx));
620aa772005SRobert Watson if (tlsctx == NULL) {
621aa772005SRobert Watson pjdlog_warning("Unable to allocate memory.");
622aa772005SRobert Watson return (ENOMEM);
623aa772005SRobert Watson }
624aa772005SRobert Watson
625aa772005SRobert Watson laddr = strdup(lstaddr);
626aa772005SRobert Watson if (laddr == NULL) {
627aa772005SRobert Watson free(tlsctx);
628aa772005SRobert Watson pjdlog_warning("Unable to allocate memory.");
629aa772005SRobert Watson return (ENOMEM);
630aa772005SRobert Watson }
631aa772005SRobert Watson bcopy("tcp://", laddr, 6);
632aa772005SRobert Watson
633aa772005SRobert Watson if (proto_server(laddr, &tcp) == -1) {
634aa772005SRobert Watson error = errno;
635aa772005SRobert Watson free(tlsctx);
636aa772005SRobert Watson free(laddr);
637aa772005SRobert Watson return (error);
638aa772005SRobert Watson }
639aa772005SRobert Watson free(laddr);
640aa772005SRobert Watson
641aa772005SRobert Watson tlsctx->tls_sock = NULL;
642aa772005SRobert Watson tlsctx->tls_tcp = tcp;
643aa772005SRobert Watson tlsctx->tls_side = TLS_SIDE_SERVER_LISTEN;
644aa772005SRobert Watson tlsctx->tls_wait_called = true;
645aa772005SRobert Watson tlsctx->tls_magic = TLS_CTX_MAGIC;
646aa772005SRobert Watson *ctxp = tlsctx;
647aa772005SRobert Watson
648aa772005SRobert Watson return (0);
649aa772005SRobert Watson }
650aa772005SRobert Watson
651aa772005SRobert Watson static void
tls_exec_server(const char * user,int startfd,const char * privkey,const char * cert,int debuglevel)652aa772005SRobert Watson tls_exec_server(const char *user, int startfd, const char *privkey,
653aa772005SRobert Watson const char *cert, int debuglevel)
654aa772005SRobert Watson {
655aa772005SRobert Watson SSL_CTX *sslctx;
656aa772005SRobert Watson SSL *ssl;
657aa772005SRobert Watson int sockfd, tcpfd, ret;
658aa772005SRobert Watson
659aa772005SRobert Watson pjdlog_debug_set(debuglevel);
660aa772005SRobert Watson pjdlog_prefix_set("[TLS sandbox] (server) ");
661aa772005SRobert Watson #ifdef HAVE_SETPROCTITLE
662aa772005SRobert Watson setproctitle("[TLS sandbox] (server) ");
663aa772005SRobert Watson #endif
664aa772005SRobert Watson
665aa772005SRobert Watson sockfd = startfd;
666aa772005SRobert Watson tcpfd = startfd + 1;
667aa772005SRobert Watson
668*b01c10d2SEd Maste #if OPENSSL_VERSION_NUMBER < 0x10100000L
669aa772005SRobert Watson SSL_load_error_strings();
670aa772005SRobert Watson SSL_library_init();
671*b01c10d2SEd Maste #endif
672aa772005SRobert Watson
6734a189b08SJung-uk Kim sslctx = SSL_CTX_new(TLS_server_method());
674aa772005SRobert Watson if (sslctx == NULL)
675aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "SSL_CTX_new() failed.");
676aa772005SRobert Watson
677aa772005SRobert Watson SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
678aa772005SRobert Watson
679aa772005SRobert Watson ssl = SSL_new(sslctx);
680aa772005SRobert Watson if (ssl == NULL)
681aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "SSL_new() failed.");
682aa772005SRobert Watson
683aa772005SRobert Watson if (SSL_use_RSAPrivateKey_file(ssl, privkey, SSL_FILETYPE_PEM) != 1) {
684aa772005SRobert Watson ssl_log_errors();
685aa772005SRobert Watson pjdlog_exitx(EX_CONFIG,
686aa772005SRobert Watson "SSL_use_RSAPrivateKey_file(%s) failed.", privkey);
687aa772005SRobert Watson }
688aa772005SRobert Watson
689aa772005SRobert Watson if (SSL_use_certificate_file(ssl, cert, SSL_FILETYPE_PEM) != 1) {
690aa772005SRobert Watson ssl_log_errors();
691aa772005SRobert Watson pjdlog_exitx(EX_CONFIG, "SSL_use_certificate_file(%s) failed.",
692aa772005SRobert Watson cert);
693aa772005SRobert Watson }
694aa772005SRobert Watson
695aa772005SRobert Watson if (sandbox(user, true, "proto_tls server") != 0)
696aa772005SRobert Watson pjdlog_exitx(EX_CONFIG, "Unable to sandbox TLS server.");
697aa772005SRobert Watson pjdlog_debug(1, "Privileges successfully dropped.");
698aa772005SRobert Watson
699aa772005SRobert Watson nonblock(sockfd);
700aa772005SRobert Watson nonblock(tcpfd);
701aa772005SRobert Watson
702aa772005SRobert Watson if (SSL_set_fd(ssl, tcpfd) != 1)
703aa772005SRobert Watson pjdlog_exitx(EX_TEMPFAIL, "SSL_set_fd() failed.");
704aa772005SRobert Watson
705aa772005SRobert Watson ret = SSL_accept(ssl);
706aa772005SRobert Watson ssl_check_error(ssl, ret);
707aa772005SRobert Watson
708aa772005SRobert Watson tls_loop(sockfd, ssl);
709aa772005SRobert Watson }
710aa772005SRobert Watson
711aa772005SRobert Watson static void
tls_call_exec_server(struct proto_conn * sock,struct proto_conn * tcp)712aa772005SRobert Watson tls_call_exec_server(struct proto_conn *sock, struct proto_conn *tcp)
713aa772005SRobert Watson {
714aa772005SRobert Watson int startfd, sockfd, tcpfd, safefd;
715aa772005SRobert Watson char *startfdstr, *debugstr;
716aa772005SRobert Watson
717aa772005SRobert Watson if (pjdlog_mode_get() == PJDLOG_MODE_STD)
718aa772005SRobert Watson startfd = 3;
719aa772005SRobert Watson else /* if (pjdlog_mode_get() == PJDLOG_MODE_SYSLOG) */
720aa772005SRobert Watson startfd = 0;
721aa772005SRobert Watson
722aa772005SRobert Watson /* Declare that we are receiver. */
723aa772005SRobert Watson proto_send(sock, NULL, 0);
724aa772005SRobert Watson
725aa772005SRobert Watson sockfd = proto_descriptor(sock);
726aa772005SRobert Watson tcpfd = proto_descriptor(tcp);
727aa772005SRobert Watson
728aa772005SRobert Watson safefd = MAX(sockfd, tcpfd);
729aa772005SRobert Watson safefd = MAX(safefd, startfd);
730aa772005SRobert Watson safefd++;
731aa772005SRobert Watson
732aa772005SRobert Watson /* Move sockfd and tcpfd to safe numbers first. */
733aa772005SRobert Watson if (dup2(sockfd, safefd) == -1)
734aa772005SRobert Watson pjdlog_exit(EX_OSERR, "dup2() failed");
735aa772005SRobert Watson proto_close(sock);
736aa772005SRobert Watson sockfd = safefd;
737aa772005SRobert Watson if (dup2(tcpfd, safefd + 1) == -1)
738aa772005SRobert Watson pjdlog_exit(EX_OSERR, "dup2() failed");
739aa772005SRobert Watson proto_close(tcp);
740aa772005SRobert Watson tcpfd = safefd + 1;
741aa772005SRobert Watson
742aa772005SRobert Watson /* Move socketpair descriptor to descriptor number startfd. */
743aa772005SRobert Watson if (dup2(sockfd, startfd) == -1)
744aa772005SRobert Watson pjdlog_exit(EX_OSERR, "dup2() failed");
745aa772005SRobert Watson (void)close(sockfd);
746aa772005SRobert Watson /* Move tcp descriptor to descriptor number startfd + 1. */
747aa772005SRobert Watson if (dup2(tcpfd, startfd + 1) == -1)
748aa772005SRobert Watson pjdlog_exit(EX_OSERR, "dup2() failed");
749aa772005SRobert Watson (void)close(tcpfd);
750aa772005SRobert Watson
751aa772005SRobert Watson closefrom(startfd + 2);
752aa772005SRobert Watson
753aa772005SRobert Watson /*
754aa772005SRobert Watson * Even if FD_CLOEXEC was set on descriptors before dup2(), it should
755aa772005SRobert Watson * have been cleared on dup2(), but better be safe than sorry.
756aa772005SRobert Watson */
757aa772005SRobert Watson if (fcntl(startfd, F_SETFD, 0) == -1)
758aa772005SRobert Watson pjdlog_exit(EX_OSERR, "fcntl() failed");
759aa772005SRobert Watson if (fcntl(startfd + 1, F_SETFD, 0) == -1)
760aa772005SRobert Watson pjdlog_exit(EX_OSERR, "fcntl() failed");
761aa772005SRobert Watson
762aa772005SRobert Watson if (asprintf(&startfdstr, "%d", startfd) == -1)
763aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "asprintf() failed");
764aa772005SRobert Watson if (asprintf(&debugstr, "%d", pjdlog_debug_get()) == -1)
765aa772005SRobert Watson pjdlog_exit(EX_TEMPFAIL, "asprintf() failed");
766aa772005SRobert Watson
767aa772005SRobert Watson execl(proto_get("execpath"), proto_get("execpath"), "proto", "tls",
768aa772005SRobert Watson proto_get("user"), "server", startfdstr, proto_get("tls:keyfile"),
769aa772005SRobert Watson proto_get("tls:certfile"), debugstr, NULL);
770aa772005SRobert Watson pjdlog_exit(EX_SOFTWARE, "execl() failed");
771aa772005SRobert Watson }
772aa772005SRobert Watson
773aa772005SRobert Watson static int
tls_accept(void * ctx,void ** newctxp)774aa772005SRobert Watson tls_accept(void *ctx, void **newctxp)
775aa772005SRobert Watson {
776aa772005SRobert Watson struct tls_ctx *tlsctx = ctx;
777aa772005SRobert Watson struct tls_ctx *newtlsctx;
778aa772005SRobert Watson struct proto_conn *sock, *tcp;
779aa772005SRobert Watson pid_t pid;
780aa772005SRobert Watson int error;
781aa772005SRobert Watson
782aa772005SRobert Watson PJDLOG_ASSERT(tlsctx != NULL);
783aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
784aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_side == TLS_SIDE_SERVER_LISTEN);
785aa772005SRobert Watson
786aa772005SRobert Watson if (proto_connect(NULL, "socketpair://", -1, &sock) == -1)
787aa772005SRobert Watson return (errno);
788aa772005SRobert Watson
789aa772005SRobert Watson /* Accept TCP connection. */
790aa772005SRobert Watson if (proto_accept(tlsctx->tls_tcp, &tcp) == -1) {
791aa772005SRobert Watson error = errno;
792aa772005SRobert Watson proto_close(sock);
793aa772005SRobert Watson return (error);
794aa772005SRobert Watson }
795aa772005SRobert Watson
796aa772005SRobert Watson pid = fork();
797aa772005SRobert Watson switch (pid) {
798aa772005SRobert Watson case -1:
799aa772005SRobert Watson /* Failure. */
800aa772005SRobert Watson error = errno;
801aa772005SRobert Watson proto_close(sock);
802aa772005SRobert Watson return (error);
803aa772005SRobert Watson case 0:
804aa772005SRobert Watson /* Child. */
805aa772005SRobert Watson pjdlog_prefix_set("[TLS sandbox] (server) ");
806aa772005SRobert Watson #ifdef HAVE_SETPROCTITLE
807aa772005SRobert Watson setproctitle("[TLS sandbox] (server) ");
808aa772005SRobert Watson #endif
809aa772005SRobert Watson /* Close listen socket. */
810aa772005SRobert Watson proto_close(tlsctx->tls_tcp);
811aa772005SRobert Watson tls_call_exec_server(sock, tcp);
812aa772005SRobert Watson /* NOTREACHED */
813aa772005SRobert Watson PJDLOG_ABORT("Unreachable.");
814aa772005SRobert Watson default:
815aa772005SRobert Watson /* Parent. */
816aa772005SRobert Watson newtlsctx = calloc(1, sizeof(*tlsctx));
817aa772005SRobert Watson if (newtlsctx == NULL) {
818aa772005SRobert Watson error = errno;
819aa772005SRobert Watson proto_close(sock);
820aa772005SRobert Watson proto_close(tcp);
821aa772005SRobert Watson (void)kill(pid, SIGKILL);
822aa772005SRobert Watson return (error);
823aa772005SRobert Watson }
824aa772005SRobert Watson proto_local_address(tcp, newtlsctx->tls_laddr,
825aa772005SRobert Watson sizeof(newtlsctx->tls_laddr));
826aa772005SRobert Watson PJDLOG_ASSERT(strncmp(newtlsctx->tls_laddr, "tcp://", 6) == 0);
827aa772005SRobert Watson bcopy("tls://", newtlsctx->tls_laddr, 6);
828aa772005SRobert Watson *strrchr(newtlsctx->tls_laddr, ':') = '\0';
829aa772005SRobert Watson proto_remote_address(tcp, newtlsctx->tls_raddr,
830aa772005SRobert Watson sizeof(newtlsctx->tls_raddr));
831aa772005SRobert Watson PJDLOG_ASSERT(strncmp(newtlsctx->tls_raddr, "tcp://", 6) == 0);
832aa772005SRobert Watson bcopy("tls://", newtlsctx->tls_raddr, 6);
833aa772005SRobert Watson *strrchr(newtlsctx->tls_raddr, ':') = '\0';
834aa772005SRobert Watson proto_close(tcp);
835aa772005SRobert Watson proto_recv(sock, NULL, 0);
836aa772005SRobert Watson newtlsctx->tls_sock = sock;
837aa772005SRobert Watson newtlsctx->tls_tcp = NULL;
838aa772005SRobert Watson newtlsctx->tls_wait_called = true;
839aa772005SRobert Watson newtlsctx->tls_side = TLS_SIDE_SERVER_WORK;
840aa772005SRobert Watson newtlsctx->tls_magic = TLS_CTX_MAGIC;
841aa772005SRobert Watson *newctxp = newtlsctx;
842aa772005SRobert Watson return (0);
843aa772005SRobert Watson }
844aa772005SRobert Watson }
845aa772005SRobert Watson
846aa772005SRobert Watson static int
tls_wrap(int fd,bool client,void ** ctxp)847aa772005SRobert Watson tls_wrap(int fd, bool client, void **ctxp)
848aa772005SRobert Watson {
849aa772005SRobert Watson struct tls_ctx *tlsctx;
850aa772005SRobert Watson struct proto_conn *sock;
851aa772005SRobert Watson int error;
852aa772005SRobert Watson
853aa772005SRobert Watson tlsctx = calloc(1, sizeof(*tlsctx));
854aa772005SRobert Watson if (tlsctx == NULL)
855aa772005SRobert Watson return (errno);
856aa772005SRobert Watson
857aa772005SRobert Watson if (proto_wrap("socketpair", client, fd, &sock) == -1) {
858aa772005SRobert Watson error = errno;
859aa772005SRobert Watson free(tlsctx);
860aa772005SRobert Watson return (error);
861aa772005SRobert Watson }
862aa772005SRobert Watson
863aa772005SRobert Watson tlsctx->tls_sock = sock;
864aa772005SRobert Watson tlsctx->tls_tcp = NULL;
865aa772005SRobert Watson tlsctx->tls_wait_called = (client ? false : true);
866aa772005SRobert Watson tlsctx->tls_side = (client ? TLS_SIDE_CLIENT : TLS_SIDE_SERVER_WORK);
867aa772005SRobert Watson tlsctx->tls_magic = TLS_CTX_MAGIC;
868aa772005SRobert Watson *ctxp = tlsctx;
869aa772005SRobert Watson
870aa772005SRobert Watson return (0);
871aa772005SRobert Watson }
872aa772005SRobert Watson
873aa772005SRobert Watson static int
tls_send(void * ctx,const unsigned char * data,size_t size,int fd)874aa772005SRobert Watson tls_send(void *ctx, const unsigned char *data, size_t size, int fd)
875aa772005SRobert Watson {
876aa772005SRobert Watson struct tls_ctx *tlsctx = ctx;
877aa772005SRobert Watson
878aa772005SRobert Watson PJDLOG_ASSERT(tlsctx != NULL);
879aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
880aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_side == TLS_SIDE_CLIENT ||
881aa772005SRobert Watson tlsctx->tls_side == TLS_SIDE_SERVER_WORK);
882aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
883aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_wait_called);
884aa772005SRobert Watson PJDLOG_ASSERT(fd == -1);
885aa772005SRobert Watson
886aa772005SRobert Watson if (proto_send(tlsctx->tls_sock, data, size) == -1)
887aa772005SRobert Watson return (errno);
888aa772005SRobert Watson
889aa772005SRobert Watson return (0);
890aa772005SRobert Watson }
891aa772005SRobert Watson
892aa772005SRobert Watson static int
tls_recv(void * ctx,unsigned char * data,size_t size,int * fdp)893aa772005SRobert Watson tls_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
894aa772005SRobert Watson {
895aa772005SRobert Watson struct tls_ctx *tlsctx = ctx;
896aa772005SRobert Watson
897aa772005SRobert Watson PJDLOG_ASSERT(tlsctx != NULL);
898aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
899aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_side == TLS_SIDE_CLIENT ||
900aa772005SRobert Watson tlsctx->tls_side == TLS_SIDE_SERVER_WORK);
901aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
902aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_wait_called);
903aa772005SRobert Watson PJDLOG_ASSERT(fdp == NULL);
904aa772005SRobert Watson
905aa772005SRobert Watson if (proto_recv(tlsctx->tls_sock, data, size) == -1)
906aa772005SRobert Watson return (errno);
907aa772005SRobert Watson
908aa772005SRobert Watson return (0);
909aa772005SRobert Watson }
910aa772005SRobert Watson
911aa772005SRobert Watson static int
tls_descriptor(const void * ctx)912aa772005SRobert Watson tls_descriptor(const void *ctx)
913aa772005SRobert Watson {
914aa772005SRobert Watson const struct tls_ctx *tlsctx = ctx;
915aa772005SRobert Watson
916aa772005SRobert Watson PJDLOG_ASSERT(tlsctx != NULL);
917aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
918aa772005SRobert Watson
919aa772005SRobert Watson switch (tlsctx->tls_side) {
920aa772005SRobert Watson case TLS_SIDE_CLIENT:
921aa772005SRobert Watson case TLS_SIDE_SERVER_WORK:
922aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
923aa772005SRobert Watson
924aa772005SRobert Watson return (proto_descriptor(tlsctx->tls_sock));
925aa772005SRobert Watson case TLS_SIDE_SERVER_LISTEN:
926aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_tcp != NULL);
927aa772005SRobert Watson
928aa772005SRobert Watson return (proto_descriptor(tlsctx->tls_tcp));
929aa772005SRobert Watson default:
930aa772005SRobert Watson PJDLOG_ABORT("Invalid side (%d).", tlsctx->tls_side);
931aa772005SRobert Watson }
932aa772005SRobert Watson }
933aa772005SRobert Watson
934aa772005SRobert Watson static bool
tcp_address_match(const void * ctx,const char * addr)935aa772005SRobert Watson tcp_address_match(const void *ctx, const char *addr)
936aa772005SRobert Watson {
937aa772005SRobert Watson const struct tls_ctx *tlsctx = ctx;
938aa772005SRobert Watson
939aa772005SRobert Watson PJDLOG_ASSERT(tlsctx != NULL);
940aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
941aa772005SRobert Watson
942aa772005SRobert Watson return (strcmp(tlsctx->tls_raddr, addr) == 0);
943aa772005SRobert Watson }
944aa772005SRobert Watson
945aa772005SRobert Watson static void
tls_local_address(const void * ctx,char * addr,size_t size)946aa772005SRobert Watson tls_local_address(const void *ctx, char *addr, size_t size)
947aa772005SRobert Watson {
948aa772005SRobert Watson const struct tls_ctx *tlsctx = ctx;
949aa772005SRobert Watson
950aa772005SRobert Watson PJDLOG_ASSERT(tlsctx != NULL);
951aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
952aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_wait_called);
953aa772005SRobert Watson
954aa772005SRobert Watson switch (tlsctx->tls_side) {
955aa772005SRobert Watson case TLS_SIDE_CLIENT:
956aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
957aa772005SRobert Watson
958aa772005SRobert Watson PJDLOG_VERIFY(strlcpy(addr, "tls://N/A", size) < size);
959aa772005SRobert Watson break;
960aa772005SRobert Watson case TLS_SIDE_SERVER_WORK:
961aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
962aa772005SRobert Watson
963aa772005SRobert Watson PJDLOG_VERIFY(strlcpy(addr, tlsctx->tls_laddr, size) < size);
964aa772005SRobert Watson break;
965aa772005SRobert Watson case TLS_SIDE_SERVER_LISTEN:
966aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_tcp != NULL);
967aa772005SRobert Watson
968aa772005SRobert Watson proto_local_address(tlsctx->tls_tcp, addr, size);
969aa772005SRobert Watson PJDLOG_ASSERT(strncmp(addr, "tcp://", 6) == 0);
970aa772005SRobert Watson /* Replace tcp:// prefix with tls:// */
971aa772005SRobert Watson bcopy("tls://", addr, 6);
972aa772005SRobert Watson break;
973aa772005SRobert Watson default:
974aa772005SRobert Watson PJDLOG_ABORT("Invalid side (%d).", tlsctx->tls_side);
975aa772005SRobert Watson }
976aa772005SRobert Watson }
977aa772005SRobert Watson
978aa772005SRobert Watson static void
tls_remote_address(const void * ctx,char * addr,size_t size)979aa772005SRobert Watson tls_remote_address(const void *ctx, char *addr, size_t size)
980aa772005SRobert Watson {
981aa772005SRobert Watson const struct tls_ctx *tlsctx = ctx;
982aa772005SRobert Watson
983aa772005SRobert Watson PJDLOG_ASSERT(tlsctx != NULL);
984aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
985aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_wait_called);
986aa772005SRobert Watson
987aa772005SRobert Watson switch (tlsctx->tls_side) {
988aa772005SRobert Watson case TLS_SIDE_CLIENT:
989aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
990aa772005SRobert Watson
991aa772005SRobert Watson PJDLOG_VERIFY(strlcpy(addr, "tls://N/A", size) < size);
992aa772005SRobert Watson break;
993aa772005SRobert Watson case TLS_SIDE_SERVER_WORK:
994aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
995aa772005SRobert Watson
996aa772005SRobert Watson PJDLOG_VERIFY(strlcpy(addr, tlsctx->tls_raddr, size) < size);
997aa772005SRobert Watson break;
998aa772005SRobert Watson case TLS_SIDE_SERVER_LISTEN:
999aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_tcp != NULL);
1000aa772005SRobert Watson
1001aa772005SRobert Watson proto_remote_address(tlsctx->tls_tcp, addr, size);
1002aa772005SRobert Watson PJDLOG_ASSERT(strncmp(addr, "tcp://", 6) == 0);
1003aa772005SRobert Watson /* Replace tcp:// prefix with tls:// */
1004aa772005SRobert Watson bcopy("tls://", addr, 6);
1005aa772005SRobert Watson break;
1006aa772005SRobert Watson default:
1007aa772005SRobert Watson PJDLOG_ABORT("Invalid side (%d).", tlsctx->tls_side);
1008aa772005SRobert Watson }
1009aa772005SRobert Watson }
1010aa772005SRobert Watson
1011aa772005SRobert Watson static void
tls_close(void * ctx)1012aa772005SRobert Watson tls_close(void *ctx)
1013aa772005SRobert Watson {
1014aa772005SRobert Watson struct tls_ctx *tlsctx = ctx;
1015aa772005SRobert Watson
1016aa772005SRobert Watson PJDLOG_ASSERT(tlsctx != NULL);
1017aa772005SRobert Watson PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
1018aa772005SRobert Watson
1019aa772005SRobert Watson if (tlsctx->tls_sock != NULL) {
1020aa772005SRobert Watson proto_close(tlsctx->tls_sock);
1021aa772005SRobert Watson tlsctx->tls_sock = NULL;
1022aa772005SRobert Watson }
1023aa772005SRobert Watson if (tlsctx->tls_tcp != NULL) {
1024aa772005SRobert Watson proto_close(tlsctx->tls_tcp);
1025aa772005SRobert Watson tlsctx->tls_tcp = NULL;
1026aa772005SRobert Watson }
1027aa772005SRobert Watson tlsctx->tls_side = 0;
1028aa772005SRobert Watson tlsctx->tls_magic = 0;
1029aa772005SRobert Watson free(tlsctx);
1030aa772005SRobert Watson }
1031aa772005SRobert Watson
1032aa772005SRobert Watson static int
tls_exec(int argc,char * argv[])1033aa772005SRobert Watson tls_exec(int argc, char *argv[])
1034aa772005SRobert Watson {
1035aa772005SRobert Watson
1036aa772005SRobert Watson PJDLOG_ASSERT(argc > 3);
1037aa772005SRobert Watson PJDLOG_ASSERT(strcmp(argv[0], "tls") == 0);
1038aa772005SRobert Watson
1039aa772005SRobert Watson pjdlog_init(atoi(argv[3]) == 0 ? PJDLOG_MODE_SYSLOG : PJDLOG_MODE_STD);
1040aa772005SRobert Watson
1041aa772005SRobert Watson if (strcmp(argv[2], "client") == 0) {
1042aa772005SRobert Watson if (argc != 10)
1043aa772005SRobert Watson return (EINVAL);
1044aa772005SRobert Watson tls_exec_client(argv[1], atoi(argv[3]),
1045aa772005SRobert Watson argv[4][0] == '\0' ? NULL : argv[4], argv[5], argv[6],
1046aa772005SRobert Watson argv[7], atoi(argv[8]), atoi(argv[9]));
1047aa772005SRobert Watson } else if (strcmp(argv[2], "server") == 0) {
1048aa772005SRobert Watson if (argc != 7)
1049aa772005SRobert Watson return (EINVAL);
1050aa772005SRobert Watson tls_exec_server(argv[1], atoi(argv[3]), argv[4], argv[5],
1051aa772005SRobert Watson atoi(argv[6]));
1052aa772005SRobert Watson }
1053aa772005SRobert Watson return (EINVAL);
1054aa772005SRobert Watson }
1055aa772005SRobert Watson
1056aa772005SRobert Watson static struct proto tls_proto = {
1057aa772005SRobert Watson .prt_name = "tls",
1058aa772005SRobert Watson .prt_connect = tls_connect,
1059aa772005SRobert Watson .prt_connect_wait = tls_connect_wait,
1060aa772005SRobert Watson .prt_server = tls_server,
1061aa772005SRobert Watson .prt_accept = tls_accept,
1062aa772005SRobert Watson .prt_wrap = tls_wrap,
1063aa772005SRobert Watson .prt_send = tls_send,
1064aa772005SRobert Watson .prt_recv = tls_recv,
1065aa772005SRobert Watson .prt_descriptor = tls_descriptor,
1066aa772005SRobert Watson .prt_address_match = tcp_address_match,
1067aa772005SRobert Watson .prt_local_address = tls_local_address,
1068aa772005SRobert Watson .prt_remote_address = tls_remote_address,
1069aa772005SRobert Watson .prt_close = tls_close,
1070aa772005SRobert Watson .prt_exec = tls_exec
1071aa772005SRobert Watson };
1072aa772005SRobert Watson
1073aa772005SRobert Watson static __constructor void
tls_ctor(void)1074aa772005SRobert Watson tls_ctor(void)
1075aa772005SRobert Watson {
1076aa772005SRobert Watson
1077aa772005SRobert Watson proto_register(&tls_proto, false);
1078aa772005SRobert Watson }
1079