xref: /openbsd/usr.sbin/relayd/check_tls.c (revision bce5c5dd)
1*bce5c5ddSclaudio /*	$OpenBSD: check_tls.c,v 1.3 2023/07/03 09:38:08 claudio Exp $	*/
285e5f500Sclaudio 
385e5f500Sclaudio /*
485e5f500Sclaudio  * Copyright (c) 2017 Claudio Jeker <claudio@openbsd.org>
585e5f500Sclaudio  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
685e5f500Sclaudio  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
785e5f500Sclaudio  *
885e5f500Sclaudio  * Permission to use, copy, modify, and distribute this software for any
985e5f500Sclaudio  * purpose with or without fee is hereby granted, provided that the above
1085e5f500Sclaudio  * copyright notice and this permission notice appear in all copies.
1185e5f500Sclaudio  *
1285e5f500Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1385e5f500Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1485e5f500Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1585e5f500Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1685e5f500Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1785e5f500Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1885e5f500Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1985e5f500Sclaudio  */
2085e5f500Sclaudio 
2185e5f500Sclaudio #include <sys/types.h>
2285e5f500Sclaudio #include <sys/queue.h>
2385e5f500Sclaudio #include <sys/uio.h>
2485e5f500Sclaudio 
2585e5f500Sclaudio #include <limits.h>
2685e5f500Sclaudio #include <event.h>
2785e5f500Sclaudio #include <unistd.h>
2885e5f500Sclaudio #include <string.h>
2985e5f500Sclaudio #include <imsg.h>
3085e5f500Sclaudio 
3185e5f500Sclaudio #include "relayd.h"
3285e5f500Sclaudio 
3385e5f500Sclaudio void	check_tls_read(int, short, void *);
3485e5f500Sclaudio void	check_tls_write(int, short, void *);
3585e5f500Sclaudio void	check_tls_handshake(int, short, void *);
3685e5f500Sclaudio void	check_tls_cleanup(struct ctl_tcp_event *);
3785e5f500Sclaudio void	check_tls_error(struct ctl_tcp_event *, const char *, const char *);
3885e5f500Sclaudio 
3985e5f500Sclaudio void
check_tls_read(int s,short event,void * arg)4085e5f500Sclaudio check_tls_read(int s, short event, void *arg)
4185e5f500Sclaudio {
4285e5f500Sclaudio 	char			 rbuf[SMALL_READ_BUF_SIZE];
4385e5f500Sclaudio 	struct ctl_tcp_event	*cte = arg;
4485e5f500Sclaudio 	int			 retry_flag = EV_READ;
4585e5f500Sclaudio 	int			 ret;
4685e5f500Sclaudio 
4785e5f500Sclaudio 	if (event == EV_TIMEOUT) {
4885e5f500Sclaudio 		cte->host->up = HOST_DOWN;
4985e5f500Sclaudio 		check_tls_cleanup(cte);
5085e5f500Sclaudio 		hce_notify_done(cte->host, HCE_TLS_READ_TIMEOUT);
5185e5f500Sclaudio 		return;
5285e5f500Sclaudio 	}
5385e5f500Sclaudio 
5485e5f500Sclaudio 	bzero(rbuf, sizeof(rbuf));
5585e5f500Sclaudio 
5685e5f500Sclaudio 	ret = tls_read(cte->tls, rbuf, sizeof(rbuf));
5785e5f500Sclaudio 	if (ret > 0) {
5885e5f500Sclaudio 		if (ibuf_add(cte->buf, rbuf, ret) == -1)
5985e5f500Sclaudio 			fatal("check_tls_read: buf_add error");
6085e5f500Sclaudio 		if (cte->validate_read != NULL &&
6185e5f500Sclaudio 		    cte->validate_read(cte) == 0) {
6285e5f500Sclaudio 			check_tls_cleanup(cte);
6385e5f500Sclaudio 			hce_notify_done(cte->host, cte->host->he);
6485e5f500Sclaudio 			return;
6585e5f500Sclaudio 		}
6685e5f500Sclaudio 	} else if (ret == 0) {
6785e5f500Sclaudio 		cte->host->up = HOST_DOWN;
6885e5f500Sclaudio 		(void)cte->validate_close(cte);
6985e5f500Sclaudio 		check_tls_cleanup(cte);
7085e5f500Sclaudio 		hce_notify_done(cte->host, cte->host->he);
7185e5f500Sclaudio 		return;
7285e5f500Sclaudio 	} else if (ret == TLS_WANT_POLLIN) {
7385e5f500Sclaudio 		retry_flag = EV_READ;
7485e5f500Sclaudio 	} else if (ret == TLS_WANT_POLLOUT) {
7585e5f500Sclaudio 		retry_flag = EV_WRITE;
7685e5f500Sclaudio 	} else {
7785e5f500Sclaudio 		cte->host->up = HOST_DOWN;
7885e5f500Sclaudio 		check_tls_error(cte, cte->host->conf.name, "cannot read");
7985e5f500Sclaudio 		check_tls_cleanup(cte);
8085e5f500Sclaudio 		hce_notify_done(cte->host, HCE_TLS_READ_ERROR);
8185e5f500Sclaudio 		return;
8285e5f500Sclaudio 	}
8385e5f500Sclaudio 
8485e5f500Sclaudio 	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, check_tls_read,
8585e5f500Sclaudio 	    &cte->tv_start, &cte->table->conf.timeout, cte);
8685e5f500Sclaudio 	return;
8785e5f500Sclaudio }
8885e5f500Sclaudio 
8985e5f500Sclaudio void
check_tls_write(int s,short event,void * arg)9085e5f500Sclaudio check_tls_write(int s, short event, void *arg)
9185e5f500Sclaudio {
9285e5f500Sclaudio 	struct ctl_tcp_event	*cte = arg;
9385e5f500Sclaudio 	int			 retry_flag = EV_WRITE;
9485e5f500Sclaudio 	int			 len;
9585e5f500Sclaudio 	int			 ret;
963f229715Srob 	void			*buf;
9785e5f500Sclaudio 
9885e5f500Sclaudio 	if (event == EV_TIMEOUT) {
9985e5f500Sclaudio 		cte->host->up = HOST_DOWN;
10085e5f500Sclaudio 		check_tls_cleanup(cte);
10185e5f500Sclaudio 		hce_notify_done(cte->host, HCE_TLS_WRITE_TIMEOUT);
10285e5f500Sclaudio 		return;
10385e5f500Sclaudio 	}
10485e5f500Sclaudio 
1053f229715Srob 	if (cte->table->sendbinbuf != NULL) {
1063f229715Srob 		len = ibuf_size(cte->table->sendbinbuf);
107*bce5c5ddSclaudio 		buf = ibuf_data(cte->table->sendbinbuf);
1083f229715Srob 		log_debug("%s: table %s sending binary", __func__,
1093f229715Srob 		    cte->table->conf.name);
110*bce5c5ddSclaudio 		print_hex(buf, 0, len);
1113f229715Srob 	} else {
11285e5f500Sclaudio 		len = strlen(cte->table->sendbuf);
1133f229715Srob 		buf = cte->table->sendbuf;
1143f229715Srob 	}
11585e5f500Sclaudio 
1163f229715Srob 	ret = tls_write(cte->tls, buf, len);
1173f229715Srob 
11885e5f500Sclaudio 	if (ret > 0) {
11985e5f500Sclaudio 		if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) ==
12085e5f500Sclaudio 		    NULL)
12185e5f500Sclaudio 			fatalx("ssl_write: cannot create dynamic buffer");
12285e5f500Sclaudio 
12385e5f500Sclaudio 		event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, check_tls_read,
12485e5f500Sclaudio 		    &cte->tv_start, &cte->table->conf.timeout, cte);
12585e5f500Sclaudio 		return;
12685e5f500Sclaudio 	} else if (ret == TLS_WANT_POLLIN) {
12785e5f500Sclaudio 		retry_flag = EV_READ;
12885e5f500Sclaudio 	} else if (ret == TLS_WANT_POLLOUT) {
12985e5f500Sclaudio 		retry_flag = EV_WRITE;
13085e5f500Sclaudio 	} else {
13185e5f500Sclaudio 		cte->host->up = HOST_DOWN;
13285e5f500Sclaudio 		check_tls_error(cte, cte->host->conf.name, "cannot write");
13385e5f500Sclaudio 		check_tls_cleanup(cte);
13485e5f500Sclaudio 		hce_notify_done(cte->host, HCE_TLS_WRITE_ERROR);
13585e5f500Sclaudio 		return;
13685e5f500Sclaudio 	}
13785e5f500Sclaudio 
13885e5f500Sclaudio 	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, check_tls_write,
13985e5f500Sclaudio 	    &cte->tv_start, &cte->table->conf.timeout, cte);
14085e5f500Sclaudio }
14185e5f500Sclaudio 
14285e5f500Sclaudio void
check_tls_handshake(int fd,short event,void * arg)14385e5f500Sclaudio check_tls_handshake(int fd, short event, void *arg)
14485e5f500Sclaudio {
14585e5f500Sclaudio 	struct ctl_tcp_event	*cte = arg;
14685e5f500Sclaudio 	int			 retry_flag = 0;
14785e5f500Sclaudio 	int			 ret;
14885e5f500Sclaudio 
14985e5f500Sclaudio 	if (event == EV_TIMEOUT) {
15085e5f500Sclaudio 		cte->host->up = HOST_DOWN;
15185e5f500Sclaudio 		hce_notify_done(cte->host, HCE_TLS_CONNECT_TIMEOUT);
15285e5f500Sclaudio 		check_tls_cleanup(cte);
15385e5f500Sclaudio 		return;
15485e5f500Sclaudio 	}
15585e5f500Sclaudio 
15685e5f500Sclaudio 	ret = tls_handshake(cte->tls);
15785e5f500Sclaudio 	if (ret == 0) {
15885e5f500Sclaudio 		if (cte->table->conf.check == CHECK_TCP) {
15985e5f500Sclaudio 			cte->host->up = HOST_UP;
16085e5f500Sclaudio 			hce_notify_done(cte->host, HCE_TLS_CONNECT_OK);
16185e5f500Sclaudio 			check_tls_cleanup(cte);
16285e5f500Sclaudio 			return;
16385e5f500Sclaudio 		}
16485e5f500Sclaudio 		if (cte->table->sendbuf != NULL) {
16585e5f500Sclaudio 			event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE,
16685e5f500Sclaudio 			    check_tls_write, &cte->tv_start,
16785e5f500Sclaudio 			    &cte->table->conf.timeout, cte);
16885e5f500Sclaudio 			return;
16985e5f500Sclaudio 		}
17085e5f500Sclaudio 		if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) ==
17185e5f500Sclaudio 		    NULL)
17285e5f500Sclaudio 			fatalx("ssl_connect: cannot create dynamic buffer");
17385e5f500Sclaudio 		event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ,
17485e5f500Sclaudio 		    check_tls_read, &cte->tv_start, &cte->table->conf.timeout,
17585e5f500Sclaudio 		    cte);
17685e5f500Sclaudio 		return;
17785e5f500Sclaudio 	} else if (ret == TLS_WANT_POLLIN) {
17885e5f500Sclaudio 		retry_flag = EV_READ;
17985e5f500Sclaudio 	} else if (ret == TLS_WANT_POLLOUT) {
18085e5f500Sclaudio 		retry_flag = EV_WRITE;
18185e5f500Sclaudio 	} else {
18285e5f500Sclaudio 		cte->host->up = HOST_DOWN;
18385e5f500Sclaudio 		check_tls_error(cte, cte->host->conf.name,
18485e5f500Sclaudio 		   "cannot connect");
18585e5f500Sclaudio 		hce_notify_done(cte->host, HCE_TLS_CONNECT_FAIL);
18685e5f500Sclaudio 		check_tls_cleanup(cte);
18785e5f500Sclaudio 		return;
18885e5f500Sclaudio 	}
18985e5f500Sclaudio 
19085e5f500Sclaudio 	event_again(&cte->ev, cte->s, EV_TIMEOUT|retry_flag,
19185e5f500Sclaudio 	    check_tls_handshake,
19285e5f500Sclaudio 	    &cte->tv_start, &cte->table->conf.timeout, cte);
19385e5f500Sclaudio }
19485e5f500Sclaudio 
19585e5f500Sclaudio void
check_tls_cleanup(struct ctl_tcp_event * cte)19685e5f500Sclaudio check_tls_cleanup(struct ctl_tcp_event *cte)
19785e5f500Sclaudio {
19885e5f500Sclaudio 	tls_close(cte->tls);
19985e5f500Sclaudio 	tls_reset(cte->tls);
20085e5f500Sclaudio 	close(cte->s);
20185e5f500Sclaudio 	ibuf_free(cte->buf);
20285e5f500Sclaudio 	cte->buf = NULL;
20385e5f500Sclaudio }
20485e5f500Sclaudio 
20585e5f500Sclaudio void
check_tls_error(struct ctl_tcp_event * cte,const char * where,const char * what)20685e5f500Sclaudio check_tls_error(struct ctl_tcp_event *cte, const char *where, const char *what)
20785e5f500Sclaudio {
20885e5f500Sclaudio 	if (log_getverbose() < 2)
20985e5f500Sclaudio 		return;
21085e5f500Sclaudio 	log_debug("TLS error: %s: %s: %s", where, what, tls_error(cte->tls));
21185e5f500Sclaudio }
21285e5f500Sclaudio 
21385e5f500Sclaudio void
check_tls(struct ctl_tcp_event * cte)21485e5f500Sclaudio check_tls(struct ctl_tcp_event *cte)
21585e5f500Sclaudio {
21685e5f500Sclaudio 	if (cte->tls == NULL) {
21785e5f500Sclaudio 		cte->tls = tls_client();
21885e5f500Sclaudio 		if (cte->tls == NULL)
21985e5f500Sclaudio 			fatal("cannot create TLS connection");
22085e5f500Sclaudio 	}
22185e5f500Sclaudio 	/* need to re-configure because of tls_reset */
22285e5f500Sclaudio 	if (tls_configure(cte->tls, cte->table->tls_cfg) == -1)
22385e5f500Sclaudio 		fatal("cannot configure TLS connection");
22485e5f500Sclaudio 
22585e5f500Sclaudio 	if (tls_connect_socket(cte->tls, cte->s, NULL) == -1) {
22685e5f500Sclaudio 		check_tls_error(cte, cte->host->conf.name,
22785e5f500Sclaudio 		    "cannot connect");
22885e5f500Sclaudio 		tls_close(cte->tls);
22985e5f500Sclaudio 		tls_reset(cte->tls);
23085e5f500Sclaudio 		cte->host->up = HOST_UNKNOWN;
23185e5f500Sclaudio 		hce_notify_done(cte->host, HCE_TLS_CONNECT_ERROR);
23285e5f500Sclaudio 		return;
23385e5f500Sclaudio 	}
23485e5f500Sclaudio 
23585e5f500Sclaudio 	event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, check_tls_handshake,
23685e5f500Sclaudio 	    &cte->tv_start, &cte->table->conf.timeout, cte);
23785e5f500Sclaudio }
238