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