xref: /openbsd/usr.sbin/relayd/check_tls.c (revision bce5c5dd)
1 /*	$OpenBSD: check_tls.c,v 1.3 2023/07/03 09:38:08 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 2017 Claudio Jeker <claudio@openbsd.org>
5  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
6  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/queue.h>
23 #include <sys/uio.h>
24 
25 #include <limits.h>
26 #include <event.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <imsg.h>
30 
31 #include "relayd.h"
32 
33 void	check_tls_read(int, short, void *);
34 void	check_tls_write(int, short, void *);
35 void	check_tls_handshake(int, short, void *);
36 void	check_tls_cleanup(struct ctl_tcp_event *);
37 void	check_tls_error(struct ctl_tcp_event *, const char *, const char *);
38 
39 void
check_tls_read(int s,short event,void * arg)40 check_tls_read(int s, short event, void *arg)
41 {
42 	char			 rbuf[SMALL_READ_BUF_SIZE];
43 	struct ctl_tcp_event	*cte = arg;
44 	int			 retry_flag = EV_READ;
45 	int			 ret;
46 
47 	if (event == EV_TIMEOUT) {
48 		cte->host->up = HOST_DOWN;
49 		check_tls_cleanup(cte);
50 		hce_notify_done(cte->host, HCE_TLS_READ_TIMEOUT);
51 		return;
52 	}
53 
54 	bzero(rbuf, sizeof(rbuf));
55 
56 	ret = tls_read(cte->tls, rbuf, sizeof(rbuf));
57 	if (ret > 0) {
58 		if (ibuf_add(cte->buf, rbuf, ret) == -1)
59 			fatal("check_tls_read: buf_add error");
60 		if (cte->validate_read != NULL &&
61 		    cte->validate_read(cte) == 0) {
62 			check_tls_cleanup(cte);
63 			hce_notify_done(cte->host, cte->host->he);
64 			return;
65 		}
66 	} else if (ret == 0) {
67 		cte->host->up = HOST_DOWN;
68 		(void)cte->validate_close(cte);
69 		check_tls_cleanup(cte);
70 		hce_notify_done(cte->host, cte->host->he);
71 		return;
72 	} else if (ret == TLS_WANT_POLLIN) {
73 		retry_flag = EV_READ;
74 	} else if (ret == TLS_WANT_POLLOUT) {
75 		retry_flag = EV_WRITE;
76 	} else {
77 		cte->host->up = HOST_DOWN;
78 		check_tls_error(cte, cte->host->conf.name, "cannot read");
79 		check_tls_cleanup(cte);
80 		hce_notify_done(cte->host, HCE_TLS_READ_ERROR);
81 		return;
82 	}
83 
84 	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, check_tls_read,
85 	    &cte->tv_start, &cte->table->conf.timeout, cte);
86 	return;
87 }
88 
89 void
check_tls_write(int s,short event,void * arg)90 check_tls_write(int s, short event, void *arg)
91 {
92 	struct ctl_tcp_event	*cte = arg;
93 	int			 retry_flag = EV_WRITE;
94 	int			 len;
95 	int			 ret;
96 	void			*buf;
97 
98 	if (event == EV_TIMEOUT) {
99 		cte->host->up = HOST_DOWN;
100 		check_tls_cleanup(cte);
101 		hce_notify_done(cte->host, HCE_TLS_WRITE_TIMEOUT);
102 		return;
103 	}
104 
105 	if (cte->table->sendbinbuf != NULL) {
106 		len = ibuf_size(cte->table->sendbinbuf);
107 		buf = ibuf_data(cte->table->sendbinbuf);
108 		log_debug("%s: table %s sending binary", __func__,
109 		    cte->table->conf.name);
110 		print_hex(buf, 0, len);
111 	} else {
112 		len = strlen(cte->table->sendbuf);
113 		buf = cte->table->sendbuf;
114 	}
115 
116 	ret = tls_write(cte->tls, buf, len);
117 
118 	if (ret > 0) {
119 		if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) ==
120 		    NULL)
121 			fatalx("ssl_write: cannot create dynamic buffer");
122 
123 		event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, check_tls_read,
124 		    &cte->tv_start, &cte->table->conf.timeout, cte);
125 		return;
126 	} else if (ret == TLS_WANT_POLLIN) {
127 		retry_flag = EV_READ;
128 	} else if (ret == TLS_WANT_POLLOUT) {
129 		retry_flag = EV_WRITE;
130 	} else {
131 		cte->host->up = HOST_DOWN;
132 		check_tls_error(cte, cte->host->conf.name, "cannot write");
133 		check_tls_cleanup(cte);
134 		hce_notify_done(cte->host, HCE_TLS_WRITE_ERROR);
135 		return;
136 	}
137 
138 	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, check_tls_write,
139 	    &cte->tv_start, &cte->table->conf.timeout, cte);
140 }
141 
142 void
check_tls_handshake(int fd,short event,void * arg)143 check_tls_handshake(int fd, short event, void *arg)
144 {
145 	struct ctl_tcp_event	*cte = arg;
146 	int			 retry_flag = 0;
147 	int			 ret;
148 
149 	if (event == EV_TIMEOUT) {
150 		cte->host->up = HOST_DOWN;
151 		hce_notify_done(cte->host, HCE_TLS_CONNECT_TIMEOUT);
152 		check_tls_cleanup(cte);
153 		return;
154 	}
155 
156 	ret = tls_handshake(cte->tls);
157 	if (ret == 0) {
158 		if (cte->table->conf.check == CHECK_TCP) {
159 			cte->host->up = HOST_UP;
160 			hce_notify_done(cte->host, HCE_TLS_CONNECT_OK);
161 			check_tls_cleanup(cte);
162 			return;
163 		}
164 		if (cte->table->sendbuf != NULL) {
165 			event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE,
166 			    check_tls_write, &cte->tv_start,
167 			    &cte->table->conf.timeout, cte);
168 			return;
169 		}
170 		if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) ==
171 		    NULL)
172 			fatalx("ssl_connect: cannot create dynamic buffer");
173 		event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ,
174 		    check_tls_read, &cte->tv_start, &cte->table->conf.timeout,
175 		    cte);
176 		return;
177 	} else if (ret == TLS_WANT_POLLIN) {
178 		retry_flag = EV_READ;
179 	} else if (ret == TLS_WANT_POLLOUT) {
180 		retry_flag = EV_WRITE;
181 	} else {
182 		cte->host->up = HOST_DOWN;
183 		check_tls_error(cte, cte->host->conf.name,
184 		   "cannot connect");
185 		hce_notify_done(cte->host, HCE_TLS_CONNECT_FAIL);
186 		check_tls_cleanup(cte);
187 		return;
188 	}
189 
190 	event_again(&cte->ev, cte->s, EV_TIMEOUT|retry_flag,
191 	    check_tls_handshake,
192 	    &cte->tv_start, &cte->table->conf.timeout, cte);
193 }
194 
195 void
check_tls_cleanup(struct ctl_tcp_event * cte)196 check_tls_cleanup(struct ctl_tcp_event *cte)
197 {
198 	tls_close(cte->tls);
199 	tls_reset(cte->tls);
200 	close(cte->s);
201 	ibuf_free(cte->buf);
202 	cte->buf = NULL;
203 }
204 
205 void
check_tls_error(struct ctl_tcp_event * cte,const char * where,const char * what)206 check_tls_error(struct ctl_tcp_event *cte, const char *where, const char *what)
207 {
208 	if (log_getverbose() < 2)
209 		return;
210 	log_debug("TLS error: %s: %s: %s", where, what, tls_error(cte->tls));
211 }
212 
213 void
check_tls(struct ctl_tcp_event * cte)214 check_tls(struct ctl_tcp_event *cte)
215 {
216 	if (cte->tls == NULL) {
217 		cte->tls = tls_client();
218 		if (cte->tls == NULL)
219 			fatal("cannot create TLS connection");
220 	}
221 	/* need to re-configure because of tls_reset */
222 	if (tls_configure(cte->tls, cte->table->tls_cfg) == -1)
223 		fatal("cannot configure TLS connection");
224 
225 	if (tls_connect_socket(cte->tls, cte->s, NULL) == -1) {
226 		check_tls_error(cte, cte->host->conf.name,
227 		    "cannot connect");
228 		tls_close(cte->tls);
229 		tls_reset(cte->tls);
230 		cte->host->up = HOST_UNKNOWN;
231 		hce_notify_done(cte->host, HCE_TLS_CONNECT_ERROR);
232 		return;
233 	}
234 
235 	event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, check_tls_handshake,
236 	    &cte->tv_start, &cte->table->conf.timeout, cte);
237 }
238