1243730Srwatson/*-
2243730Srwatson * Copyright (c) 2011 The FreeBSD Foundation
3243730Srwatson * All rights reserved.
4243730Srwatson *
5243730Srwatson * This software was developed by Pawel Jakub Dawidek under sponsorship from
6243730Srwatson * the FreeBSD Foundation.
7243730Srwatson *
8243730Srwatson * Redistribution and use in source and binary forms, with or without
9243730Srwatson * modification, are permitted provided that the following conditions
10243730Srwatson * are met:
11243730Srwatson * 1. Redistributions of source code must retain the above copyright
12243730Srwatson *    notice, this list of conditions and the following disclaimer.
13243730Srwatson * 2. Redistributions in binary form must reproduce the above copyright
14243730Srwatson *    notice, this list of conditions and the following disclaimer in the
15243730Srwatson *    documentation and/or other materials provided with the distribution.
16243730Srwatson *
17243730Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18243730Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19243730Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20243730Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21243730Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22243730Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23243730Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24243730Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25243730Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26243730Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27243730Srwatson * SUCH DAMAGE.
28243730Srwatson */
29243730Srwatson
30243734Srwatson#include <config/config.h>
31243730Srwatson
32243730Srwatson#include <sys/param.h>	/* MAXHOSTNAMELEN */
33243730Srwatson#include <sys/socket.h>
34243730Srwatson
35243730Srwatson#include <arpa/inet.h>
36243730Srwatson
37243730Srwatson#include <netinet/in.h>
38243730Srwatson#include <netinet/tcp.h>
39243730Srwatson
40243730Srwatson#include <errno.h>
41243730Srwatson#include <fcntl.h>
42243730Srwatson#include <netdb.h>
43243730Srwatson#include <signal.h>
44243730Srwatson#include <stdbool.h>
45243730Srwatson#include <stdint.h>
46243730Srwatson#include <stdio.h>
47243730Srwatson#include <string.h>
48243730Srwatson#include <unistd.h>
49243730Srwatson
50243730Srwatson#include <openssl/err.h>
51243730Srwatson#include <openssl/ssl.h>
52243730Srwatson
53243730Srwatson#include <compat/compat.h>
54243730Srwatson#ifndef HAVE_CLOSEFROM
55243730Srwatson#include <compat/closefrom.h>
56243730Srwatson#endif
57243730Srwatson#ifndef HAVE_STRLCPY
58243730Srwatson#include <compat/strlcpy.h>
59243730Srwatson#endif
60243730Srwatson
61243730Srwatson#include "pjdlog.h"
62243730Srwatson#include "proto_impl.h"
63243730Srwatson#include "sandbox.h"
64243730Srwatson#include "subr.h"
65243730Srwatson
66243730Srwatson#define	TLS_CTX_MAGIC	0x715c7
67243730Srwatsonstruct tls_ctx {
68243730Srwatson	int		tls_magic;
69243730Srwatson	struct proto_conn *tls_sock;
70243730Srwatson	struct proto_conn *tls_tcp;
71243730Srwatson	char		tls_laddr[256];
72243730Srwatson	char		tls_raddr[256];
73243730Srwatson	int		tls_side;
74243730Srwatson#define	TLS_SIDE_CLIENT		0
75243730Srwatson#define	TLS_SIDE_SERVER_LISTEN	1
76243730Srwatson#define	TLS_SIDE_SERVER_WORK	2
77243730Srwatson	bool		tls_wait_called;
78243730Srwatson};
79243730Srwatson
80243730Srwatson#define	TLS_DEFAULT_TIMEOUT	30
81243730Srwatson
82243730Srwatsonstatic int tls_connect_wait(void *ctx, int timeout);
83243730Srwatsonstatic void tls_close(void *ctx);
84243730Srwatson
85243730Srwatsonstatic void
86243730Srwatsonblock(int fd)
87243730Srwatson{
88243730Srwatson	int flags;
89243730Srwatson
90243730Srwatson	flags = fcntl(fd, F_GETFL);
91243730Srwatson	if (flags == -1)
92243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "fcntl(F_GETFL) failed");
93243730Srwatson	flags &= ~O_NONBLOCK;
94243730Srwatson	if (fcntl(fd, F_SETFL, flags) == -1)
95243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "fcntl(F_SETFL) failed");
96243730Srwatson}
97243730Srwatson
98243730Srwatsonstatic void
99243730Srwatsonnonblock(int fd)
100243730Srwatson{
101243730Srwatson	int flags;
102243730Srwatson
103243730Srwatson	flags = fcntl(fd, F_GETFL);
104243730Srwatson	if (flags == -1)
105243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "fcntl(F_GETFL) failed");
106243730Srwatson	flags |= O_NONBLOCK;
107243730Srwatson	if (fcntl(fd, F_SETFL, flags) == -1)
108243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "fcntl(F_SETFL) failed");
109243730Srwatson}
110243730Srwatson
111243730Srwatsonstatic int
112243730Srwatsonwait_for_fd(int fd, int timeout)
113243730Srwatson{
114243730Srwatson	struct timeval tv;
115243730Srwatson	fd_set fdset;
116243730Srwatson	int error, ret;
117243730Srwatson
118243730Srwatson	error = 0;
119243730Srwatson
120243730Srwatson	for (;;) {
121243730Srwatson		FD_ZERO(&fdset);
122243730Srwatson		FD_SET(fd, &fdset);
123243730Srwatson
124243730Srwatson		tv.tv_sec = timeout;
125243730Srwatson		tv.tv_usec = 0;
126243730Srwatson
127243730Srwatson		ret = select(fd + 1, NULL, &fdset, NULL,
128243730Srwatson		    timeout == -1 ? NULL : &tv);
129243730Srwatson		if (ret == 0) {
130243730Srwatson			error = ETIMEDOUT;
131243730Srwatson			break;
132243730Srwatson		} else if (ret == -1) {
133243730Srwatson			if (errno == EINTR)
134243730Srwatson				continue;
135243730Srwatson			error = errno;
136243730Srwatson			break;
137243730Srwatson		}
138243730Srwatson		PJDLOG_ASSERT(ret > 0);
139243730Srwatson		PJDLOG_ASSERT(FD_ISSET(fd, &fdset));
140243730Srwatson		break;
141243730Srwatson	}
142243730Srwatson
143243730Srwatson	return (error);
144243730Srwatson}
145243730Srwatson
146243730Srwatsonstatic void
147243730Srwatsonssl_log_errors(void)
148243730Srwatson{
149243730Srwatson	unsigned long error;
150243730Srwatson
151243730Srwatson	while ((error = ERR_get_error()) != 0)
152243730Srwatson		pjdlog_error("SSL error: %s", ERR_error_string(error, NULL));
153243730Srwatson}
154243730Srwatson
155243730Srwatsonstatic int
156243730Srwatsonssl_check_error(SSL *ssl, int ret)
157243730Srwatson{
158243730Srwatson	int error;
159243730Srwatson
160243730Srwatson	error = SSL_get_error(ssl, ret);
161243730Srwatson
162243730Srwatson	switch (error) {
163243730Srwatson	case SSL_ERROR_NONE:
164243730Srwatson		return (0);
165243730Srwatson	case SSL_ERROR_WANT_READ:
166243730Srwatson		pjdlog_debug(2, "SSL_ERROR_WANT_READ");
167243730Srwatson		return (-1);
168243730Srwatson	case SSL_ERROR_WANT_WRITE:
169243730Srwatson		pjdlog_debug(2, "SSL_ERROR_WANT_WRITE");
170243730Srwatson		return (-1);
171243730Srwatson	case SSL_ERROR_ZERO_RETURN:
172243730Srwatson		pjdlog_exitx(EX_OK, "Connection closed.");
173243730Srwatson	case SSL_ERROR_SYSCALL:
174243730Srwatson		ssl_log_errors();
175243730Srwatson		pjdlog_exitx(EX_TEMPFAIL, "SSL I/O error.");
176243730Srwatson	case SSL_ERROR_SSL:
177243730Srwatson		ssl_log_errors();
178243730Srwatson		pjdlog_exitx(EX_TEMPFAIL, "SSL protocol error.");
179243730Srwatson	default:
180243730Srwatson		ssl_log_errors();
181243730Srwatson		pjdlog_exitx(EX_TEMPFAIL, "Unknown SSL error (%d).", error);
182243730Srwatson	}
183243730Srwatson}
184243730Srwatson
185243730Srwatsonstatic void
186243730Srwatsontcp_recv_ssl_send(int recvfd, SSL *sendssl)
187243730Srwatson{
188243730Srwatson	static unsigned char buf[65536];
189243730Srwatson	ssize_t tcpdone;
190243730Srwatson	int sendfd, ssldone;
191243730Srwatson
192243730Srwatson	sendfd = SSL_get_fd(sendssl);
193243730Srwatson	PJDLOG_ASSERT(sendfd >= 0);
194243730Srwatson	pjdlog_debug(2, "%s: start %d -> %d", __func__, recvfd, sendfd);
195243730Srwatson	for (;;) {
196243730Srwatson		tcpdone = recv(recvfd, buf, sizeof(buf), 0);
197243730Srwatson		pjdlog_debug(2, "%s: recv() returned %zd", __func__, tcpdone);
198243730Srwatson		if (tcpdone == 0) {
199243730Srwatson			pjdlog_debug(1, "Connection terminated.");
200243730Srwatson			exit(0);
201243730Srwatson		} else if (tcpdone == -1) {
202243730Srwatson			if (errno == EINTR)
203243730Srwatson				continue;
204243730Srwatson			else if (errno == EAGAIN)
205243730Srwatson				break;
206243730Srwatson			pjdlog_exit(EX_TEMPFAIL, "recv() failed");
207243730Srwatson		}
208243730Srwatson		for (;;) {
209243730Srwatson			ssldone = SSL_write(sendssl, buf, (int)tcpdone);
210243730Srwatson			pjdlog_debug(2, "%s: send() returned %d", __func__,
211243730Srwatson			    ssldone);
212243730Srwatson			if (ssl_check_error(sendssl, ssldone) == -1) {
213243730Srwatson				(void)wait_for_fd(sendfd, -1);
214243730Srwatson				continue;
215243730Srwatson			}
216243730Srwatson			PJDLOG_ASSERT(ssldone == tcpdone);
217243730Srwatson			break;
218243730Srwatson		}
219243730Srwatson	}
220243730Srwatson	pjdlog_debug(2, "%s: done %d -> %d", __func__, recvfd, sendfd);
221243730Srwatson}
222243730Srwatson
223243730Srwatsonstatic void
224243730Srwatsonssl_recv_tcp_send(SSL *recvssl, int sendfd)
225243730Srwatson{
226243730Srwatson	static unsigned char buf[65536];
227243730Srwatson	unsigned char *ptr;
228243730Srwatson	ssize_t tcpdone;
229243730Srwatson	size_t todo;
230243730Srwatson	int recvfd, ssldone;
231243730Srwatson
232243730Srwatson	recvfd = SSL_get_fd(recvssl);
233243730Srwatson	PJDLOG_ASSERT(recvfd >= 0);
234243730Srwatson	pjdlog_debug(2, "%s: start %d -> %d", __func__, recvfd, sendfd);
235243730Srwatson	for (;;) {
236243730Srwatson		ssldone = SSL_read(recvssl, buf, sizeof(buf));
237243730Srwatson		pjdlog_debug(2, "%s: SSL_read() returned %d", __func__,
238243730Srwatson		    ssldone);
239243730Srwatson		if (ssl_check_error(recvssl, ssldone) == -1)
240243730Srwatson			break;
241243730Srwatson		todo = (size_t)ssldone;
242243730Srwatson		ptr = buf;
243243730Srwatson		do {
244243730Srwatson			tcpdone = send(sendfd, ptr, todo, MSG_NOSIGNAL);
245243730Srwatson			pjdlog_debug(2, "%s: send() returned %zd", __func__,
246243730Srwatson			    tcpdone);
247243730Srwatson			if (tcpdone == 0) {
248243730Srwatson				pjdlog_debug(1, "Connection terminated.");
249243730Srwatson				exit(0);
250243730Srwatson			} else if (tcpdone == -1) {
251243730Srwatson				if (errno == EINTR || errno == ENOBUFS)
252243730Srwatson					continue;
253243730Srwatson				if (errno == EAGAIN) {
254243730Srwatson					(void)wait_for_fd(sendfd, -1);
255243730Srwatson					continue;
256243730Srwatson				}
257243730Srwatson				pjdlog_exit(EX_TEMPFAIL, "send() failed");
258243730Srwatson			}
259243730Srwatson			todo -= tcpdone;
260243730Srwatson			ptr += tcpdone;
261243730Srwatson		} while (todo > 0);
262243730Srwatson	}
263243730Srwatson	pjdlog_debug(2, "%s: done %d -> %d", __func__, recvfd, sendfd);
264243730Srwatson}
265243730Srwatson
266243730Srwatsonstatic void
267243730Srwatsontls_loop(int sockfd, SSL *tcpssl)
268243730Srwatson{
269243730Srwatson	fd_set fds;
270243730Srwatson	int maxfd, tcpfd;
271243730Srwatson
272243730Srwatson	tcpfd = SSL_get_fd(tcpssl);
273243730Srwatson	PJDLOG_ASSERT(tcpfd >= 0);
274243730Srwatson
275243730Srwatson	for (;;) {
276243730Srwatson		FD_ZERO(&fds);
277243730Srwatson		FD_SET(sockfd, &fds);
278243730Srwatson		FD_SET(tcpfd, &fds);
279243730Srwatson		maxfd = MAX(sockfd, tcpfd);
280243730Srwatson
281243730Srwatson		PJDLOG_ASSERT(maxfd + 1 <= (int)FD_SETSIZE);
282243730Srwatson		if (select(maxfd + 1, &fds, NULL, NULL, NULL) == -1) {
283243730Srwatson			if (errno == EINTR)
284243730Srwatson				continue;
285243730Srwatson			pjdlog_exit(EX_TEMPFAIL, "select() failed");
286243730Srwatson		}
287243730Srwatson		if (FD_ISSET(sockfd, &fds))
288243730Srwatson			tcp_recv_ssl_send(sockfd, tcpssl);
289243730Srwatson		if (FD_ISSET(tcpfd, &fds))
290243730Srwatson			ssl_recv_tcp_send(tcpssl, sockfd);
291243730Srwatson	}
292243730Srwatson}
293243730Srwatson
294243730Srwatsonstatic void
295243730Srwatsontls_certificate_verify(SSL *ssl, const char *fingerprint)
296243730Srwatson{
297243730Srwatson	unsigned char md[EVP_MAX_MD_SIZE];
298243730Srwatson	char mdstr[sizeof("SHA256=") - 1 + EVP_MAX_MD_SIZE * 3];
299243730Srwatson	char *mdstrp;
300243730Srwatson	unsigned int i, mdsize;
301243730Srwatson	X509 *cert;
302243730Srwatson
303243730Srwatson	if (fingerprint[0] == '\0') {
304243730Srwatson		pjdlog_debug(1, "No fingerprint verification requested.");
305243730Srwatson		return;
306243730Srwatson	}
307243730Srwatson
308243730Srwatson	cert = SSL_get_peer_certificate(ssl);
309243730Srwatson	if (cert == NULL)
310243730Srwatson		pjdlog_exitx(EX_TEMPFAIL, "No peer certificate received.");
311243730Srwatson
312243730Srwatson	if (X509_digest(cert, EVP_sha256(), md, &mdsize) != 1)
313243730Srwatson		pjdlog_exitx(EX_TEMPFAIL, "X509_digest() failed.");
314243730Srwatson	PJDLOG_ASSERT(mdsize <= EVP_MAX_MD_SIZE);
315243730Srwatson
316243730Srwatson	X509_free(cert);
317243730Srwatson
318243730Srwatson	(void)strlcpy(mdstr, "SHA256=", sizeof(mdstr));
319243730Srwatson	mdstrp = mdstr + strlen(mdstr);
320243730Srwatson	for (i = 0; i < mdsize; i++) {
321243730Srwatson		PJDLOG_VERIFY(mdstrp + 3 <= mdstr + sizeof(mdstr));
322243730Srwatson		(void)sprintf(mdstrp, "%02hhX:", md[i]);
323243730Srwatson		mdstrp += 3;
324243730Srwatson	}
325243730Srwatson	/* Clear last colon. */
326243730Srwatson	mdstrp[-1] = '\0';
327243730Srwatson	if (strcasecmp(mdstr, fingerprint) != 0) {
328243730Srwatson		pjdlog_exitx(EX_NOPERM,
329243730Srwatson		    "Finger print doesn't match. Received \"%s\", expected \"%s\"",
330243730Srwatson		    mdstr, fingerprint);
331243730Srwatson	}
332243730Srwatson}
333243730Srwatson
334243730Srwatsonstatic void
335243730Srwatsontls_exec_client(const char *user, int startfd, const char *srcaddr,
336243730Srwatson    const char *dstaddr, const char *fingerprint, const char *defport,
337243730Srwatson    int timeout, int debuglevel)
338243730Srwatson{
339243730Srwatson	struct proto_conn *tcp;
340243730Srwatson	char *saddr, *daddr;
341243730Srwatson	SSL_CTX *sslctx;
342243730Srwatson	SSL *ssl;
343243730Srwatson	long ret;
344243730Srwatson	int sockfd, tcpfd;
345243730Srwatson	uint8_t connected;
346243730Srwatson
347243730Srwatson	pjdlog_debug_set(debuglevel);
348243730Srwatson	pjdlog_prefix_set("[TLS sandbox] (client) ");
349243730Srwatson#ifdef HAVE_SETPROCTITLE
350243730Srwatson	setproctitle("[TLS sandbox] (client) ");
351243730Srwatson#endif
352243730Srwatson	proto_set("tcp:port", defport);
353243730Srwatson
354243730Srwatson	sockfd = startfd;
355243730Srwatson
356243730Srwatson	/* Change tls:// to tcp://. */
357243730Srwatson	if (srcaddr == NULL) {
358243730Srwatson		saddr = NULL;
359243730Srwatson	} else {
360243730Srwatson		saddr = strdup(srcaddr);
361243730Srwatson		if (saddr == NULL)
362243730Srwatson			pjdlog_exitx(EX_TEMPFAIL, "Unable to allocate memory.");
363243730Srwatson		bcopy("tcp://", saddr, 6);
364243730Srwatson	}
365243730Srwatson	daddr = strdup(dstaddr);
366243730Srwatson	if (daddr == NULL)
367243730Srwatson		pjdlog_exitx(EX_TEMPFAIL, "Unable to allocate memory.");
368243730Srwatson	bcopy("tcp://", daddr, 6);
369243730Srwatson
370243730Srwatson	/* Establish TCP connection. */
371243730Srwatson	if (proto_connect(saddr, daddr, timeout, &tcp) == -1)
372243730Srwatson		exit(EX_TEMPFAIL);
373243730Srwatson
374243730Srwatson	SSL_load_error_strings();
375243730Srwatson	SSL_library_init();
376243730Srwatson
377243730Srwatson	/*
378243730Srwatson	 * TODO: On FreeBSD we could move this below sandbox() once libc and
379243730Srwatson	 *       libcrypto use sysctl kern.arandom to obtain random data
380243730Srwatson	 *       instead of /dev/urandom and friends.
381243730Srwatson	 */
382243730Srwatson	sslctx = SSL_CTX_new(TLSv1_client_method());
383243730Srwatson	if (sslctx == NULL)
384243730Srwatson		pjdlog_exitx(EX_TEMPFAIL, "SSL_CTX_new() failed.");
385243730Srwatson
386243730Srwatson	if (sandbox(user, true, "proto_tls client: %s", dstaddr) != 0)
387243730Srwatson		pjdlog_exitx(EX_CONFIG, "Unable to sandbox TLS client.");
388243730Srwatson	pjdlog_debug(1, "Privileges successfully dropped.");
389243730Srwatson
390243730Srwatson	SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
391243730Srwatson
392243730Srwatson	/* Load CA certs. */
393243730Srwatson	/* TODO */
394243730Srwatson	//SSL_CTX_load_verify_locations(sslctx, cacerts_file, NULL);
395243730Srwatson
396243730Srwatson	ssl = SSL_new(sslctx);
397243730Srwatson	if (ssl == NULL)
398243730Srwatson		pjdlog_exitx(EX_TEMPFAIL, "SSL_new() failed.");
399243730Srwatson
400243730Srwatson	tcpfd = proto_descriptor(tcp);
401243730Srwatson
402243730Srwatson	block(tcpfd);
403243730Srwatson
404243730Srwatson	if (SSL_set_fd(ssl, tcpfd) != 1)
405243730Srwatson		pjdlog_exitx(EX_TEMPFAIL, "SSL_set_fd() failed.");
406243730Srwatson
407243730Srwatson	ret = SSL_connect(ssl);
408243730Srwatson	ssl_check_error(ssl, (int)ret);
409243730Srwatson
410243730Srwatson	nonblock(sockfd);
411243730Srwatson	nonblock(tcpfd);
412243730Srwatson
413243730Srwatson	tls_certificate_verify(ssl, fingerprint);
414243730Srwatson
415243730Srwatson	/*
416243730Srwatson	 * The following byte is send to make proto_connect_wait() to work.
417243730Srwatson	 */
418243730Srwatson	connected = 1;
419243730Srwatson	for (;;) {
420243730Srwatson		switch (send(sockfd, &connected, sizeof(connected), 0)) {
421243730Srwatson		case -1:
422243730Srwatson			if (errno == EINTR || errno == ENOBUFS)
423243730Srwatson				continue;
424243730Srwatson			if (errno == EAGAIN) {
425243730Srwatson				(void)wait_for_fd(sockfd, -1);
426243730Srwatson				continue;
427243730Srwatson			}
428243730Srwatson			pjdlog_exit(EX_TEMPFAIL, "send() failed");
429243730Srwatson		case 0:
430243730Srwatson			pjdlog_debug(1, "Connection terminated.");
431243730Srwatson			exit(0);
432243730Srwatson		case 1:
433243730Srwatson			break;
434243730Srwatson		}
435243730Srwatson		break;
436243730Srwatson	}
437243730Srwatson
438243730Srwatson	tls_loop(sockfd, ssl);
439243730Srwatson}
440243730Srwatson
441243730Srwatsonstatic void
442243730Srwatsontls_call_exec_client(struct proto_conn *sock, const char *srcaddr,
443243730Srwatson    const char *dstaddr, int timeout)
444243730Srwatson{
445243730Srwatson	char *timeoutstr, *startfdstr, *debugstr;
446243730Srwatson	int startfd;
447243730Srwatson
448243730Srwatson	/* Declare that we are receiver. */
449243730Srwatson	proto_recv(sock, NULL, 0);
450243730Srwatson
451243730Srwatson	if (pjdlog_mode_get() == PJDLOG_MODE_STD)
452243730Srwatson		startfd = 3;
453243730Srwatson	else /* if (pjdlog_mode_get() == PJDLOG_MODE_SYSLOG) */
454243730Srwatson		startfd = 0;
455243730Srwatson
456243730Srwatson	if (proto_descriptor(sock) != startfd) {
457243730Srwatson		/* Move socketpair descriptor to descriptor number startfd. */
458243730Srwatson		if (dup2(proto_descriptor(sock), startfd) == -1)
459243730Srwatson			pjdlog_exit(EX_OSERR, "dup2() failed");
460243730Srwatson		proto_close(sock);
461243730Srwatson	} else {
462243730Srwatson		/*
463243730Srwatson		 * The FD_CLOEXEC is cleared by dup2(2), so when we not
464243730Srwatson		 * call it, we have to clear it by hand in case it is set.
465243730Srwatson		 */
466243730Srwatson		if (fcntl(startfd, F_SETFD, 0) == -1)
467243730Srwatson			pjdlog_exit(EX_OSERR, "fcntl() failed");
468243730Srwatson	}
469243730Srwatson
470243730Srwatson	closefrom(startfd + 1);
471243730Srwatson
472243730Srwatson	if (asprintf(&startfdstr, "%d", startfd) == -1)
473243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "asprintf() failed");
474243730Srwatson	if (timeout == -1)
475243730Srwatson		timeout = TLS_DEFAULT_TIMEOUT;
476243730Srwatson	if (asprintf(&timeoutstr, "%d", timeout) == -1)
477243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "asprintf() failed");
478243730Srwatson	if (asprintf(&debugstr, "%d", pjdlog_debug_get()) == -1)
479243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "asprintf() failed");
480243730Srwatson
481243730Srwatson	execl(proto_get("execpath"), proto_get("execpath"), "proto", "tls",
482243730Srwatson	    proto_get("user"), "client", startfdstr,
483243730Srwatson	    srcaddr == NULL ? "" : srcaddr, dstaddr,
484243730Srwatson	    proto_get("tls:fingerprint"), proto_get("tcp:port"), timeoutstr,
485243730Srwatson	    debugstr, NULL);
486243730Srwatson	pjdlog_exit(EX_SOFTWARE, "execl() failed");
487243730Srwatson}
488243730Srwatson
489243730Srwatsonstatic int
490243730Srwatsontls_connect(const char *srcaddr, const char *dstaddr, int timeout, void **ctxp)
491243730Srwatson{
492243730Srwatson	struct tls_ctx *tlsctx;
493243730Srwatson	struct proto_conn *sock;
494243730Srwatson	pid_t pid;
495243730Srwatson	int error;
496243730Srwatson
497243730Srwatson	PJDLOG_ASSERT(srcaddr == NULL || srcaddr[0] != '\0');
498243730Srwatson	PJDLOG_ASSERT(dstaddr != NULL);
499243730Srwatson	PJDLOG_ASSERT(timeout >= -1);
500243730Srwatson	PJDLOG_ASSERT(ctxp != NULL);
501243730Srwatson
502243730Srwatson	if (strncmp(dstaddr, "tls://", 6) != 0)
503243730Srwatson		return (-1);
504243730Srwatson	if (srcaddr != NULL && strncmp(srcaddr, "tls://", 6) != 0)
505243730Srwatson		return (-1);
506243730Srwatson
507243730Srwatson	if (proto_connect(NULL, "socketpair://", -1, &sock) == -1)
508243730Srwatson		return (errno);
509243730Srwatson
510243730Srwatson#if 0
511243730Srwatson	/*
512243730Srwatson	 * We use rfork() with the following flags to disable SIGCHLD
513243730Srwatson	 * delivery upon the sandbox process exit.
514243730Srwatson	 */
515243730Srwatson	pid = rfork(RFFDG | RFPROC | RFTSIGZMB | RFTSIGFLAGS(0));
516243730Srwatson#else
517243730Srwatson	/*
518243730Srwatson	 * We don't use rfork() to be able to log information about sandbox
519243730Srwatson	 * process exiting.
520243730Srwatson	 */
521243730Srwatson	pid = fork();
522243730Srwatson#endif
523243730Srwatson	switch (pid) {
524243730Srwatson	case -1:
525243730Srwatson		/* Failure. */
526243730Srwatson		error = errno;
527243730Srwatson		proto_close(sock);
528243730Srwatson		return (error);
529243730Srwatson	case 0:
530243730Srwatson		/* Child. */
531243730Srwatson		pjdlog_prefix_set("[TLS sandbox] (client) ");
532243730Srwatson#ifdef HAVE_SETPROCTITLE
533243730Srwatson		setproctitle("[TLS sandbox] (client) ");
534243730Srwatson#endif
535243730Srwatson		tls_call_exec_client(sock, srcaddr, dstaddr, timeout);
536243730Srwatson		/* NOTREACHED */
537243730Srwatson	default:
538243730Srwatson		/* Parent. */
539243730Srwatson		tlsctx = calloc(1, sizeof(*tlsctx));
540243730Srwatson		if (tlsctx == NULL) {
541243730Srwatson			error = errno;
542243730Srwatson			proto_close(sock);
543243730Srwatson			(void)kill(pid, SIGKILL);
544243730Srwatson			return (error);
545243730Srwatson		}
546243730Srwatson		proto_send(sock, NULL, 0);
547243730Srwatson		tlsctx->tls_sock = sock;
548243730Srwatson		tlsctx->tls_tcp = NULL;
549243730Srwatson		tlsctx->tls_side = TLS_SIDE_CLIENT;
550243730Srwatson		tlsctx->tls_wait_called = false;
551243730Srwatson		tlsctx->tls_magic = TLS_CTX_MAGIC;
552243730Srwatson		if (timeout >= 0) {
553243730Srwatson			error = tls_connect_wait(tlsctx, timeout);
554243730Srwatson			if (error != 0) {
555243730Srwatson				(void)kill(pid, SIGKILL);
556243730Srwatson				tls_close(tlsctx);
557243730Srwatson				return (error);
558243730Srwatson			}
559243730Srwatson		}
560243730Srwatson		*ctxp = tlsctx;
561243730Srwatson		return (0);
562243730Srwatson	}
563243730Srwatson}
564243730Srwatson
565243730Srwatsonstatic int
566243730Srwatsontls_connect_wait(void *ctx, int timeout)
567243730Srwatson{
568243730Srwatson	struct tls_ctx *tlsctx = ctx;
569243730Srwatson	int error, sockfd;
570243730Srwatson	uint8_t connected;
571243730Srwatson
572243730Srwatson	PJDLOG_ASSERT(tlsctx != NULL);
573243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
574243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_side == TLS_SIDE_CLIENT);
575243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
576243730Srwatson	PJDLOG_ASSERT(!tlsctx->tls_wait_called);
577243730Srwatson	PJDLOG_ASSERT(timeout >= 0);
578243730Srwatson
579243730Srwatson	sockfd = proto_descriptor(tlsctx->tls_sock);
580243730Srwatson	error = wait_for_fd(sockfd, timeout);
581243730Srwatson	if (error != 0)
582243730Srwatson		return (error);
583243730Srwatson
584243730Srwatson	for (;;) {
585243730Srwatson		switch (recv(sockfd, &connected, sizeof(connected),
586243730Srwatson		    MSG_WAITALL)) {
587243730Srwatson		case -1:
588243730Srwatson			if (errno == EINTR || errno == ENOBUFS)
589243730Srwatson				continue;
590243730Srwatson			error = errno;
591243730Srwatson			break;
592243730Srwatson		case 0:
593243730Srwatson			pjdlog_debug(1, "Connection terminated.");
594243730Srwatson			error = ENOTCONN;
595243730Srwatson			break;
596243730Srwatson		case 1:
597243730Srwatson			tlsctx->tls_wait_called = true;
598243730Srwatson			break;
599243730Srwatson		}
600243730Srwatson		break;
601243730Srwatson	}
602243730Srwatson
603243730Srwatson	return (error);
604243730Srwatson}
605243730Srwatson
606243730Srwatsonstatic int
607243730Srwatsontls_server(const char *lstaddr, void **ctxp)
608243730Srwatson{
609243730Srwatson	struct proto_conn *tcp;
610243730Srwatson	struct tls_ctx *tlsctx;
611243730Srwatson	char *laddr;
612243730Srwatson	int error;
613243730Srwatson
614243730Srwatson	if (strncmp(lstaddr, "tls://", 6) != 0)
615243730Srwatson		return (-1);
616243730Srwatson
617243730Srwatson	tlsctx = malloc(sizeof(*tlsctx));
618243730Srwatson	if (tlsctx == NULL) {
619243730Srwatson		pjdlog_warning("Unable to allocate memory.");
620243730Srwatson		return (ENOMEM);
621243730Srwatson	}
622243730Srwatson
623243730Srwatson	laddr = strdup(lstaddr);
624243730Srwatson	if (laddr == NULL) {
625243730Srwatson		free(tlsctx);
626243730Srwatson		pjdlog_warning("Unable to allocate memory.");
627243730Srwatson		return (ENOMEM);
628243730Srwatson	}
629243730Srwatson	bcopy("tcp://", laddr, 6);
630243730Srwatson
631243730Srwatson	if (proto_server(laddr, &tcp) == -1) {
632243730Srwatson		error = errno;
633243730Srwatson		free(tlsctx);
634243730Srwatson		free(laddr);
635243730Srwatson		return (error);
636243730Srwatson	}
637243730Srwatson	free(laddr);
638243730Srwatson
639243730Srwatson	tlsctx->tls_sock = NULL;
640243730Srwatson	tlsctx->tls_tcp = tcp;
641243730Srwatson	tlsctx->tls_side = TLS_SIDE_SERVER_LISTEN;
642243730Srwatson	tlsctx->tls_wait_called = true;
643243730Srwatson	tlsctx->tls_magic = TLS_CTX_MAGIC;
644243730Srwatson	*ctxp = tlsctx;
645243730Srwatson
646243730Srwatson	return (0);
647243730Srwatson}
648243730Srwatson
649243730Srwatsonstatic void
650243730Srwatsontls_exec_server(const char *user, int startfd, const char *privkey,
651243730Srwatson    const char *cert, int debuglevel)
652243730Srwatson{
653243730Srwatson	SSL_CTX *sslctx;
654243730Srwatson	SSL *ssl;
655243730Srwatson	int sockfd, tcpfd, ret;
656243730Srwatson
657243730Srwatson	pjdlog_debug_set(debuglevel);
658243730Srwatson	pjdlog_prefix_set("[TLS sandbox] (server) ");
659243730Srwatson#ifdef HAVE_SETPROCTITLE
660243730Srwatson	setproctitle("[TLS sandbox] (server) ");
661243730Srwatson#endif
662243730Srwatson
663243730Srwatson	sockfd = startfd;
664243730Srwatson	tcpfd = startfd + 1;
665243730Srwatson
666243730Srwatson	SSL_load_error_strings();
667243730Srwatson	SSL_library_init();
668243730Srwatson
669243730Srwatson	sslctx = SSL_CTX_new(TLSv1_server_method());
670243730Srwatson	if (sslctx == NULL)
671243730Srwatson		pjdlog_exitx(EX_TEMPFAIL, "SSL_CTX_new() failed.");
672243730Srwatson
673243730Srwatson	SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
674243730Srwatson
675243730Srwatson	ssl = SSL_new(sslctx);
676243730Srwatson	if (ssl == NULL)
677243730Srwatson		pjdlog_exitx(EX_TEMPFAIL, "SSL_new() failed.");
678243730Srwatson
679243730Srwatson	if (SSL_use_RSAPrivateKey_file(ssl, privkey, SSL_FILETYPE_PEM) != 1) {
680243730Srwatson		ssl_log_errors();
681243730Srwatson		pjdlog_exitx(EX_CONFIG,
682243730Srwatson		    "SSL_use_RSAPrivateKey_file(%s) failed.", privkey);
683243730Srwatson	}
684243730Srwatson
685243730Srwatson	if (SSL_use_certificate_file(ssl, cert, SSL_FILETYPE_PEM) != 1) {
686243730Srwatson		ssl_log_errors();
687243730Srwatson		pjdlog_exitx(EX_CONFIG, "SSL_use_certificate_file(%s) failed.",
688243730Srwatson		    cert);
689243730Srwatson	}
690243730Srwatson
691243730Srwatson	if (sandbox(user, true, "proto_tls server") != 0)
692243730Srwatson		pjdlog_exitx(EX_CONFIG, "Unable to sandbox TLS server.");
693243730Srwatson	pjdlog_debug(1, "Privileges successfully dropped.");
694243730Srwatson
695243730Srwatson	nonblock(sockfd);
696243730Srwatson	nonblock(tcpfd);
697243730Srwatson
698243730Srwatson	if (SSL_set_fd(ssl, tcpfd) != 1)
699243730Srwatson		pjdlog_exitx(EX_TEMPFAIL, "SSL_set_fd() failed.");
700243730Srwatson
701243730Srwatson	ret = SSL_accept(ssl);
702243730Srwatson	ssl_check_error(ssl, ret);
703243730Srwatson
704243730Srwatson	tls_loop(sockfd, ssl);
705243730Srwatson}
706243730Srwatson
707243730Srwatsonstatic void
708243730Srwatsontls_call_exec_server(struct proto_conn *sock, struct proto_conn *tcp)
709243730Srwatson{
710243730Srwatson	int startfd, sockfd, tcpfd, safefd;
711243730Srwatson	char *startfdstr, *debugstr;
712243730Srwatson
713243730Srwatson	if (pjdlog_mode_get() == PJDLOG_MODE_STD)
714243730Srwatson		startfd = 3;
715243730Srwatson	else /* if (pjdlog_mode_get() == PJDLOG_MODE_SYSLOG) */
716243730Srwatson		startfd = 0;
717243730Srwatson
718243730Srwatson	/* Declare that we are receiver. */
719243730Srwatson	proto_send(sock, NULL, 0);
720243730Srwatson
721243730Srwatson	sockfd = proto_descriptor(sock);
722243730Srwatson	tcpfd = proto_descriptor(tcp);
723243730Srwatson
724243730Srwatson	safefd = MAX(sockfd, tcpfd);
725243730Srwatson	safefd = MAX(safefd, startfd);
726243730Srwatson	safefd++;
727243730Srwatson
728243730Srwatson	/* Move sockfd and tcpfd to safe numbers first. */
729243730Srwatson	if (dup2(sockfd, safefd) == -1)
730243730Srwatson		pjdlog_exit(EX_OSERR, "dup2() failed");
731243730Srwatson	proto_close(sock);
732243730Srwatson	sockfd = safefd;
733243730Srwatson	if (dup2(tcpfd, safefd + 1) == -1)
734243730Srwatson		pjdlog_exit(EX_OSERR, "dup2() failed");
735243730Srwatson	proto_close(tcp);
736243730Srwatson	tcpfd = safefd + 1;
737243730Srwatson
738243730Srwatson	/* Move socketpair descriptor to descriptor number startfd. */
739243730Srwatson	if (dup2(sockfd, startfd) == -1)
740243730Srwatson		pjdlog_exit(EX_OSERR, "dup2() failed");
741243730Srwatson	(void)close(sockfd);
742243730Srwatson	/* Move tcp descriptor to descriptor number startfd + 1. */
743243730Srwatson	if (dup2(tcpfd, startfd + 1) == -1)
744243730Srwatson		pjdlog_exit(EX_OSERR, "dup2() failed");
745243730Srwatson	(void)close(tcpfd);
746243730Srwatson
747243730Srwatson	closefrom(startfd + 2);
748243730Srwatson
749243730Srwatson	/*
750243730Srwatson	 * Even if FD_CLOEXEC was set on descriptors before dup2(), it should
751243730Srwatson	 * have been cleared on dup2(), but better be safe than sorry.
752243730Srwatson	 */
753243730Srwatson	if (fcntl(startfd, F_SETFD, 0) == -1)
754243730Srwatson		pjdlog_exit(EX_OSERR, "fcntl() failed");
755243730Srwatson	if (fcntl(startfd + 1, F_SETFD, 0) == -1)
756243730Srwatson		pjdlog_exit(EX_OSERR, "fcntl() failed");
757243730Srwatson
758243730Srwatson	if (asprintf(&startfdstr, "%d", startfd) == -1)
759243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "asprintf() failed");
760243730Srwatson	if (asprintf(&debugstr, "%d", pjdlog_debug_get()) == -1)
761243730Srwatson		pjdlog_exit(EX_TEMPFAIL, "asprintf() failed");
762243730Srwatson
763243730Srwatson	execl(proto_get("execpath"), proto_get("execpath"), "proto", "tls",
764243730Srwatson	    proto_get("user"), "server", startfdstr, proto_get("tls:keyfile"),
765243730Srwatson	    proto_get("tls:certfile"), debugstr, NULL);
766243730Srwatson	pjdlog_exit(EX_SOFTWARE, "execl() failed");
767243730Srwatson}
768243730Srwatson
769243730Srwatsonstatic int
770243730Srwatsontls_accept(void *ctx, void **newctxp)
771243730Srwatson{
772243730Srwatson	struct tls_ctx *tlsctx = ctx;
773243730Srwatson	struct tls_ctx *newtlsctx;
774243730Srwatson	struct proto_conn *sock, *tcp;
775243730Srwatson	pid_t pid;
776243730Srwatson	int error;
777243730Srwatson
778243730Srwatson	PJDLOG_ASSERT(tlsctx != NULL);
779243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
780243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_side == TLS_SIDE_SERVER_LISTEN);
781243730Srwatson
782243730Srwatson	if (proto_connect(NULL, "socketpair://", -1, &sock) == -1)
783243730Srwatson		return (errno);
784243730Srwatson
785243730Srwatson	/* Accept TCP connection. */
786243730Srwatson	if (proto_accept(tlsctx->tls_tcp, &tcp) == -1) {
787243730Srwatson		error = errno;
788243730Srwatson		proto_close(sock);
789243730Srwatson		return (error);
790243730Srwatson	}
791243730Srwatson
792243730Srwatson	pid = fork();
793243730Srwatson	switch (pid) {
794243730Srwatson	case -1:
795243730Srwatson		/* Failure. */
796243730Srwatson		error = errno;
797243730Srwatson		proto_close(sock);
798243730Srwatson		return (error);
799243730Srwatson	case 0:
800243730Srwatson		/* Child. */
801243730Srwatson		pjdlog_prefix_set("[TLS sandbox] (server) ");
802243730Srwatson#ifdef HAVE_SETPROCTITLE
803243730Srwatson		setproctitle("[TLS sandbox] (server) ");
804243730Srwatson#endif
805243730Srwatson		/* Close listen socket. */
806243730Srwatson		proto_close(tlsctx->tls_tcp);
807243730Srwatson		tls_call_exec_server(sock, tcp);
808243730Srwatson		/* NOTREACHED */
809243730Srwatson		PJDLOG_ABORT("Unreachable.");
810243730Srwatson	default:
811243730Srwatson		/* Parent. */
812243730Srwatson		newtlsctx = calloc(1, sizeof(*tlsctx));
813243730Srwatson		if (newtlsctx == NULL) {
814243730Srwatson			error = errno;
815243730Srwatson			proto_close(sock);
816243730Srwatson			proto_close(tcp);
817243730Srwatson			(void)kill(pid, SIGKILL);
818243730Srwatson			return (error);
819243730Srwatson		}
820243730Srwatson		proto_local_address(tcp, newtlsctx->tls_laddr,
821243730Srwatson		    sizeof(newtlsctx->tls_laddr));
822243730Srwatson		PJDLOG_ASSERT(strncmp(newtlsctx->tls_laddr, "tcp://", 6) == 0);
823243730Srwatson		bcopy("tls://", newtlsctx->tls_laddr, 6);
824243730Srwatson		*strrchr(newtlsctx->tls_laddr, ':') = '\0';
825243730Srwatson		proto_remote_address(tcp, newtlsctx->tls_raddr,
826243730Srwatson		    sizeof(newtlsctx->tls_raddr));
827243730Srwatson		PJDLOG_ASSERT(strncmp(newtlsctx->tls_raddr, "tcp://", 6) == 0);
828243730Srwatson		bcopy("tls://", newtlsctx->tls_raddr, 6);
829243730Srwatson		*strrchr(newtlsctx->tls_raddr, ':') = '\0';
830243730Srwatson		proto_close(tcp);
831243730Srwatson		proto_recv(sock, NULL, 0);
832243730Srwatson		newtlsctx->tls_sock = sock;
833243730Srwatson		newtlsctx->tls_tcp = NULL;
834243730Srwatson		newtlsctx->tls_wait_called = true;
835243730Srwatson		newtlsctx->tls_side = TLS_SIDE_SERVER_WORK;
836243730Srwatson		newtlsctx->tls_magic = TLS_CTX_MAGIC;
837243730Srwatson		*newctxp = newtlsctx;
838243730Srwatson		return (0);
839243730Srwatson	}
840243730Srwatson}
841243730Srwatson
842243730Srwatsonstatic int
843243730Srwatsontls_wrap(int fd, bool client, void **ctxp)
844243730Srwatson{
845243730Srwatson	struct tls_ctx *tlsctx;
846243730Srwatson	struct proto_conn *sock;
847243730Srwatson	int error;
848243730Srwatson
849243730Srwatson	tlsctx = calloc(1, sizeof(*tlsctx));
850243730Srwatson	if (tlsctx == NULL)
851243730Srwatson		return (errno);
852243730Srwatson
853243730Srwatson	if (proto_wrap("socketpair", client, fd, &sock) == -1) {
854243730Srwatson		error = errno;
855243730Srwatson		free(tlsctx);
856243730Srwatson		return (error);
857243730Srwatson	}
858243730Srwatson
859243730Srwatson	tlsctx->tls_sock = sock;
860243730Srwatson	tlsctx->tls_tcp = NULL;
861243730Srwatson	tlsctx->tls_wait_called = (client ? false : true);
862243730Srwatson	tlsctx->tls_side = (client ? TLS_SIDE_CLIENT : TLS_SIDE_SERVER_WORK);
863243730Srwatson	tlsctx->tls_magic = TLS_CTX_MAGIC;
864243730Srwatson	*ctxp = tlsctx;
865243730Srwatson
866243730Srwatson	return (0);
867243730Srwatson}
868243730Srwatson
869243730Srwatsonstatic int
870243730Srwatsontls_send(void *ctx, const unsigned char *data, size_t size, int fd)
871243730Srwatson{
872243730Srwatson	struct tls_ctx *tlsctx = ctx;
873243730Srwatson
874243730Srwatson	PJDLOG_ASSERT(tlsctx != NULL);
875243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
876243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_side == TLS_SIDE_CLIENT ||
877243730Srwatson	    tlsctx->tls_side == TLS_SIDE_SERVER_WORK);
878243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
879243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_wait_called);
880243730Srwatson	PJDLOG_ASSERT(fd == -1);
881243730Srwatson
882243730Srwatson	if (proto_send(tlsctx->tls_sock, data, size) == -1)
883243730Srwatson		return (errno);
884243730Srwatson
885243730Srwatson	return (0);
886243730Srwatson}
887243730Srwatson
888243730Srwatsonstatic int
889243730Srwatsontls_recv(void *ctx, unsigned char *data, size_t size, int *fdp)
890243730Srwatson{
891243730Srwatson	struct tls_ctx *tlsctx = ctx;
892243730Srwatson
893243730Srwatson	PJDLOG_ASSERT(tlsctx != NULL);
894243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
895243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_side == TLS_SIDE_CLIENT ||
896243730Srwatson	    tlsctx->tls_side == TLS_SIDE_SERVER_WORK);
897243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
898243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_wait_called);
899243730Srwatson	PJDLOG_ASSERT(fdp == NULL);
900243730Srwatson
901243730Srwatson	if (proto_recv(tlsctx->tls_sock, data, size) == -1)
902243730Srwatson		return (errno);
903243730Srwatson
904243730Srwatson	return (0);
905243730Srwatson}
906243730Srwatson
907243730Srwatsonstatic int
908243730Srwatsontls_descriptor(const void *ctx)
909243730Srwatson{
910243730Srwatson	const struct tls_ctx *tlsctx = ctx;
911243730Srwatson
912243730Srwatson	PJDLOG_ASSERT(tlsctx != NULL);
913243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
914243730Srwatson
915243730Srwatson	switch (tlsctx->tls_side) {
916243730Srwatson	case TLS_SIDE_CLIENT:
917243730Srwatson	case TLS_SIDE_SERVER_WORK:
918243730Srwatson		PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
919243730Srwatson
920243730Srwatson		return (proto_descriptor(tlsctx->tls_sock));
921243730Srwatson	case TLS_SIDE_SERVER_LISTEN:
922243730Srwatson		PJDLOG_ASSERT(tlsctx->tls_tcp != NULL);
923243730Srwatson
924243730Srwatson		return (proto_descriptor(tlsctx->tls_tcp));
925243730Srwatson	default:
926243730Srwatson		PJDLOG_ABORT("Invalid side (%d).", tlsctx->tls_side);
927243730Srwatson	}
928243730Srwatson}
929243730Srwatson
930243730Srwatsonstatic bool
931243730Srwatsontcp_address_match(const void *ctx, const char *addr)
932243730Srwatson{
933243730Srwatson	const struct tls_ctx *tlsctx = ctx;
934243730Srwatson
935243730Srwatson	PJDLOG_ASSERT(tlsctx != NULL);
936243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
937243730Srwatson
938243730Srwatson	return (strcmp(tlsctx->tls_raddr, addr) == 0);
939243730Srwatson}
940243730Srwatson
941243730Srwatsonstatic void
942243730Srwatsontls_local_address(const void *ctx, char *addr, size_t size)
943243730Srwatson{
944243730Srwatson	const struct tls_ctx *tlsctx = ctx;
945243730Srwatson
946243730Srwatson	PJDLOG_ASSERT(tlsctx != NULL);
947243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
948243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_wait_called);
949243730Srwatson
950243730Srwatson	switch (tlsctx->tls_side) {
951243730Srwatson	case TLS_SIDE_CLIENT:
952243730Srwatson		PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
953243730Srwatson
954243730Srwatson		PJDLOG_VERIFY(strlcpy(addr, "tls://N/A", size) < size);
955243730Srwatson		break;
956243730Srwatson	case TLS_SIDE_SERVER_WORK:
957243730Srwatson		PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
958243730Srwatson
959243730Srwatson		PJDLOG_VERIFY(strlcpy(addr, tlsctx->tls_laddr, size) < size);
960243730Srwatson		break;
961243730Srwatson	case TLS_SIDE_SERVER_LISTEN:
962243730Srwatson		PJDLOG_ASSERT(tlsctx->tls_tcp != NULL);
963243730Srwatson
964243730Srwatson		proto_local_address(tlsctx->tls_tcp, addr, size);
965243730Srwatson		PJDLOG_ASSERT(strncmp(addr, "tcp://", 6) == 0);
966243730Srwatson		/* Replace tcp:// prefix with tls:// */
967243730Srwatson		bcopy("tls://", addr, 6);
968243730Srwatson		break;
969243730Srwatson	default:
970243730Srwatson		PJDLOG_ABORT("Invalid side (%d).", tlsctx->tls_side);
971243730Srwatson	}
972243730Srwatson}
973243730Srwatson
974243730Srwatsonstatic void
975243730Srwatsontls_remote_address(const void *ctx, char *addr, size_t size)
976243730Srwatson{
977243730Srwatson	const struct tls_ctx *tlsctx = ctx;
978243730Srwatson
979243730Srwatson	PJDLOG_ASSERT(tlsctx != NULL);
980243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
981243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_wait_called);
982243730Srwatson
983243730Srwatson	switch (tlsctx->tls_side) {
984243730Srwatson	case TLS_SIDE_CLIENT:
985243730Srwatson		PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
986243730Srwatson
987243730Srwatson		PJDLOG_VERIFY(strlcpy(addr, "tls://N/A", size) < size);
988243730Srwatson		break;
989243730Srwatson	case TLS_SIDE_SERVER_WORK:
990243730Srwatson		PJDLOG_ASSERT(tlsctx->tls_sock != NULL);
991243730Srwatson
992243730Srwatson		PJDLOG_VERIFY(strlcpy(addr, tlsctx->tls_raddr, size) < size);
993243730Srwatson		break;
994243730Srwatson	case TLS_SIDE_SERVER_LISTEN:
995243730Srwatson		PJDLOG_ASSERT(tlsctx->tls_tcp != NULL);
996243730Srwatson
997243730Srwatson		proto_remote_address(tlsctx->tls_tcp, addr, size);
998243730Srwatson		PJDLOG_ASSERT(strncmp(addr, "tcp://", 6) == 0);
999243730Srwatson		/* Replace tcp:// prefix with tls:// */
1000243730Srwatson		bcopy("tls://", addr, 6);
1001243730Srwatson		break;
1002243730Srwatson	default:
1003243730Srwatson		PJDLOG_ABORT("Invalid side (%d).", tlsctx->tls_side);
1004243730Srwatson	}
1005243730Srwatson}
1006243730Srwatson
1007243730Srwatsonstatic void
1008243730Srwatsontls_close(void *ctx)
1009243730Srwatson{
1010243730Srwatson	struct tls_ctx *tlsctx = ctx;
1011243730Srwatson
1012243730Srwatson	PJDLOG_ASSERT(tlsctx != NULL);
1013243730Srwatson	PJDLOG_ASSERT(tlsctx->tls_magic == TLS_CTX_MAGIC);
1014243730Srwatson
1015243730Srwatson	if (tlsctx->tls_sock != NULL) {
1016243730Srwatson		proto_close(tlsctx->tls_sock);
1017243730Srwatson		tlsctx->tls_sock = NULL;
1018243730Srwatson	}
1019243730Srwatson	if (tlsctx->tls_tcp != NULL) {
1020243730Srwatson		proto_close(tlsctx->tls_tcp);
1021243730Srwatson		tlsctx->tls_tcp = NULL;
1022243730Srwatson	}
1023243730Srwatson	tlsctx->tls_side = 0;
1024243730Srwatson	tlsctx->tls_magic = 0;
1025243730Srwatson	free(tlsctx);
1026243730Srwatson}
1027243730Srwatson
1028243730Srwatsonstatic int
1029243730Srwatsontls_exec(int argc, char *argv[])
1030243730Srwatson{
1031243730Srwatson
1032243730Srwatson	PJDLOG_ASSERT(argc > 3);
1033243730Srwatson	PJDLOG_ASSERT(strcmp(argv[0], "tls") == 0);
1034243730Srwatson
1035243730Srwatson	pjdlog_init(atoi(argv[3]) == 0 ? PJDLOG_MODE_SYSLOG : PJDLOG_MODE_STD);
1036243730Srwatson
1037243730Srwatson	if (strcmp(argv[2], "client") == 0) {
1038243730Srwatson		if (argc != 10)
1039243730Srwatson			return (EINVAL);
1040243730Srwatson		tls_exec_client(argv[1], atoi(argv[3]),
1041243730Srwatson		    argv[4][0] == '\0' ? NULL : argv[4], argv[5], argv[6],
1042243730Srwatson		    argv[7], atoi(argv[8]), atoi(argv[9]));
1043243730Srwatson	} else if (strcmp(argv[2], "server") == 0) {
1044243730Srwatson		if (argc != 7)
1045243730Srwatson			return (EINVAL);
1046243730Srwatson		tls_exec_server(argv[1], atoi(argv[3]), argv[4], argv[5],
1047243730Srwatson		    atoi(argv[6]));
1048243730Srwatson	}
1049243730Srwatson	return (EINVAL);
1050243730Srwatson}
1051243730Srwatson
1052243730Srwatsonstatic struct proto tls_proto = {
1053243730Srwatson	.prt_name = "tls",
1054243730Srwatson	.prt_connect = tls_connect,
1055243730Srwatson	.prt_connect_wait = tls_connect_wait,
1056243730Srwatson	.prt_server = tls_server,
1057243730Srwatson	.prt_accept = tls_accept,
1058243730Srwatson	.prt_wrap = tls_wrap,
1059243730Srwatson	.prt_send = tls_send,
1060243730Srwatson	.prt_recv = tls_recv,
1061243730Srwatson	.prt_descriptor = tls_descriptor,
1062243730Srwatson	.prt_address_match = tcp_address_match,
1063243730Srwatson	.prt_local_address = tls_local_address,
1064243730Srwatson	.prt_remote_address = tls_remote_address,
1065243730Srwatson	.prt_close = tls_close,
1066243730Srwatson	.prt_exec = tls_exec
1067243730Srwatson};
1068243730Srwatson
1069243730Srwatsonstatic __constructor void
1070243730Srwatsontls_ctor(void)
1071243730Srwatson{
1072243730Srwatson
1073243730Srwatson	proto_register(&tls_proto, false);
1074243730Srwatson}
1075