Blob


1 /* $OpenBSD: check_tls.c,v 1.4 2026/03/02 19:28:01 rsadowski Exp $ */
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 */
21 #include <sys/types.h>
22 #include <sys/queue.h>
23 #include <sys/uio.h>
25 #include <limits.h>
26 #include <event.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <imsg.h>
31 #include "relayd.h"
32 #include "log.h"
34 void check_tls_read(int, short, void *);
35 void check_tls_write(int, short, void *);
36 void check_tls_handshake(int, short, void *);
37 void check_tls_cleanup(struct ctl_tcp_event *);
38 void check_tls_error(struct ctl_tcp_event *, const char *, const char *);
40 void
41 check_tls_read(int s, short event, void *arg)
42 {
43 char rbuf[SMALL_READ_BUF_SIZE];
44 struct ctl_tcp_event *cte = arg;
45 int retry_flag = EV_READ;
46 int ret;
48 if (event == EV_TIMEOUT) {
49 cte->host->up = HOST_DOWN;
50 check_tls_cleanup(cte);
51 hce_notify_done(cte->host, HCE_TLS_READ_TIMEOUT);
52 return;
53 }
55 bzero(rbuf, sizeof(rbuf));
57 ret = tls_read(cte->tls, rbuf, sizeof(rbuf));
58 if (ret > 0) {
59 if (ibuf_add(cte->buf, rbuf, ret) == -1)
60 fatal("check_tls_read: buf_add error");
61 if (cte->validate_read != NULL &&
62 cte->validate_read(cte) == 0) {
63 check_tls_cleanup(cte);
64 hce_notify_done(cte->host, cte->host->he);
65 return;
66 }
67 } else if (ret == 0) {
68 cte->host->up = HOST_DOWN;
69 (void)cte->validate_close(cte);
70 check_tls_cleanup(cte);
71 hce_notify_done(cte->host, cte->host->he);
72 return;
73 } else if (ret == TLS_WANT_POLLIN) {
74 retry_flag = EV_READ;
75 } else if (ret == TLS_WANT_POLLOUT) {
76 retry_flag = EV_WRITE;
77 } else {
78 cte->host->up = HOST_DOWN;
79 check_tls_error(cte, cte->host->conf.name, "cannot read");
80 check_tls_cleanup(cte);
81 hce_notify_done(cte->host, HCE_TLS_READ_ERROR);
82 return;
83 }
85 event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, check_tls_read,
86 &cte->tv_start, &cte->table->conf.timeout, cte);
87 return;
88 }
90 void
91 check_tls_write(int s, short event, void *arg)
92 {
93 struct ctl_tcp_event *cte = arg;
94 int retry_flag = EV_WRITE;
95 int len;
96 int ret;
97 void *buf;
99 if (event == EV_TIMEOUT) {
100 cte->host->up = HOST_DOWN;
101 check_tls_cleanup(cte);
102 hce_notify_done(cte->host, HCE_TLS_WRITE_TIMEOUT);
103 return;
106 if (cte->table->sendbinbuf != NULL) {
107 len = ibuf_size(cte->table->sendbinbuf);
108 buf = ibuf_data(cte->table->sendbinbuf);
109 log_debug("%s: table %s sending binary", __func__,
110 cte->table->conf.name);
111 print_hex(buf, 0, len);
112 } else {
113 len = strlen(cte->table->sendbuf);
114 buf = cte->table->sendbuf;
117 ret = tls_write(cte->tls, buf, len);
119 if (ret > 0) {
120 if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) ==
121 NULL)
122 fatalx("ssl_write: cannot create dynamic buffer");
124 event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, check_tls_read,
125 &cte->tv_start, &cte->table->conf.timeout, cte);
126 return;
127 } else if (ret == TLS_WANT_POLLIN) {
128 retry_flag = EV_READ;
129 } else if (ret == TLS_WANT_POLLOUT) {
130 retry_flag = EV_WRITE;
131 } else {
132 cte->host->up = HOST_DOWN;
133 check_tls_error(cte, cte->host->conf.name, "cannot write");
134 check_tls_cleanup(cte);
135 hce_notify_done(cte->host, HCE_TLS_WRITE_ERROR);
136 return;
139 event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, check_tls_write,
140 &cte->tv_start, &cte->table->conf.timeout, cte);
143 void
144 check_tls_handshake(int fd, short event, void *arg)
146 struct ctl_tcp_event *cte = arg;
147 int retry_flag = 0;
148 int ret;
150 if (event == EV_TIMEOUT) {
151 cte->host->up = HOST_DOWN;
152 hce_notify_done(cte->host, HCE_TLS_CONNECT_TIMEOUT);
153 check_tls_cleanup(cte);
154 return;
157 ret = tls_handshake(cte->tls);
158 if (ret == 0) {
159 if (cte->table->conf.check == CHECK_TCP) {
160 cte->host->up = HOST_UP;
161 hce_notify_done(cte->host, HCE_TLS_CONNECT_OK);
162 check_tls_cleanup(cte);
163 return;
165 if (cte->table->sendbuf != NULL) {
166 event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE,
167 check_tls_write, &cte->tv_start,
168 &cte->table->conf.timeout, cte);
169 return;
171 if ((cte->buf = ibuf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) ==
172 NULL)
173 fatalx("ssl_connect: cannot create dynamic buffer");
174 event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ,
175 check_tls_read, &cte->tv_start, &cte->table->conf.timeout,
176 cte);
177 return;
178 } else if (ret == TLS_WANT_POLLIN) {
179 retry_flag = EV_READ;
180 } else if (ret == TLS_WANT_POLLOUT) {
181 retry_flag = EV_WRITE;
182 } else {
183 cte->host->up = HOST_DOWN;
184 check_tls_error(cte, cte->host->conf.name,
185 "cannot connect");
186 hce_notify_done(cte->host, HCE_TLS_CONNECT_FAIL);
187 check_tls_cleanup(cte);
188 return;
191 event_again(&cte->ev, cte->s, EV_TIMEOUT|retry_flag,
192 check_tls_handshake,
193 &cte->tv_start, &cte->table->conf.timeout, cte);
196 void
197 check_tls_cleanup(struct ctl_tcp_event *cte)
199 tls_close(cte->tls);
200 tls_reset(cte->tls);
201 close(cte->s);
202 ibuf_free(cte->buf);
203 cte->buf = NULL;
206 void
207 check_tls_error(struct ctl_tcp_event *cte, const char *where, const char *what)
209 if (log_getverbose() < 2)
210 return;
211 log_debug("TLS error: %s: %s: %s", where, what, tls_error(cte->tls));
214 void
215 check_tls(struct ctl_tcp_event *cte)
217 if (cte->tls == NULL) {
218 cte->tls = tls_client();
219 if (cte->tls == NULL)
220 fatal("cannot create TLS connection");
222 /* need to re-configure because of tls_reset */
223 if (tls_configure(cte->tls, cte->table->tls_cfg) == -1)
224 fatal("cannot configure TLS connection");
226 if (tls_connect_socket(cte->tls, cte->s, NULL) == -1) {
227 check_tls_error(cte, cte->host->conf.name,
228 "cannot connect");
229 tls_close(cte->tls);
230 tls_reset(cte->tls);
231 cte->host->up = HOST_UNKNOWN;
232 hce_notify_done(cte->host, HCE_TLS_CONNECT_ERROR);
233 return;
236 event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, check_tls_handshake,
237 &cte->tv_start, &cte->table->conf.timeout, cte);