xref: /openbsd-src/usr.sbin/syslogd/evbuffer_tls.c (revision 18e6fc661f3541231c1005c37988a2840f51dbd4)
1*18e6fc66Sbluhm /*	$OpenBSD: evbuffer_tls.c,v 1.14 2024/11/07 10:12:18 bluhm Exp $ */
2b60e1f73Sbluhm 
3b60e1f73Sbluhm /*
4b60e1f73Sbluhm  * Copyright (c) 2002-2004 Niels Provos <provos@citi.umich.edu>
5b60e1f73Sbluhm  * Copyright (c) 2014-2015 Alexander Bluhm <bluhm@openbsd.org>
6b60e1f73Sbluhm  * All rights reserved.
7b60e1f73Sbluhm  *
8b60e1f73Sbluhm  * Redistribution and use in source and binary forms, with or without
9b60e1f73Sbluhm  * modification, are permitted provided that the following conditions
10b60e1f73Sbluhm  * are met:
11b60e1f73Sbluhm  * 1. Redistributions of source code must retain the above copyright
12b60e1f73Sbluhm  *    notice, this list of conditions and the following disclaimer.
13b60e1f73Sbluhm  * 2. Redistributions in binary form must reproduce the above copyright
14b60e1f73Sbluhm  *    notice, this list of conditions and the following disclaimer in the
15b60e1f73Sbluhm  *    documentation and/or other materials provided with the distribution.
16b60e1f73Sbluhm  * 3. The name of the author may not be used to endorse or promote products
17b60e1f73Sbluhm  *    derived from this software without specific prior written permission.
18b60e1f73Sbluhm  *
19b60e1f73Sbluhm  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20b60e1f73Sbluhm  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21b60e1f73Sbluhm  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22b60e1f73Sbluhm  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23b60e1f73Sbluhm  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24b60e1f73Sbluhm  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25b60e1f73Sbluhm  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26b60e1f73Sbluhm  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27b60e1f73Sbluhm  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28b60e1f73Sbluhm  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29b60e1f73Sbluhm  */
30b60e1f73Sbluhm 
31b60e1f73Sbluhm #include <sys/types.h>
32b60e1f73Sbluhm #include <sys/time.h>
33b60e1f73Sbluhm #include <sys/ioctl.h>
34b60e1f73Sbluhm 
35b60e1f73Sbluhm #include <errno.h>
36b60e1f73Sbluhm #include <event.h>
37b60e1f73Sbluhm #include <stdio.h>
38b60e1f73Sbluhm #include <stdlib.h>
39b60e1f73Sbluhm #include <string.h>
40b60e1f73Sbluhm #include <tls.h>
41b60e1f73Sbluhm 
42b60e1f73Sbluhm #include "evbuffer_tls.h"
43b60e1f73Sbluhm 
44b60e1f73Sbluhm /* prototypes */
45b60e1f73Sbluhm 
46b60e1f73Sbluhm void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *);
47f770f76fSbluhm static void buffertls_readcb(int, short, void *);
48f770f76fSbluhm static void buffertls_writecb(int, short, void *);
49f770f76fSbluhm static void buffertls_handshakecb(int, short, void *);
50b60e1f73Sbluhm int evtls_read(struct evbuffer *, int, int, struct tls *);
51b60e1f73Sbluhm int evtls_write(struct evbuffer *, int, struct tls *);
52b60e1f73Sbluhm 
53b60e1f73Sbluhm static int
54b60e1f73Sbluhm bufferevent_add(struct event *ev, int timeout)
55b60e1f73Sbluhm {
56b60e1f73Sbluhm 	struct timeval tv, *ptv = NULL;
57b60e1f73Sbluhm 
58b60e1f73Sbluhm 	if (timeout) {
59b60e1f73Sbluhm 		timerclear(&tv);
60b60e1f73Sbluhm 		tv.tv_sec = timeout;
61b60e1f73Sbluhm 		ptv = &tv;
62b60e1f73Sbluhm 	}
63b60e1f73Sbluhm 
64b60e1f73Sbluhm 	return (event_add(ev, ptv));
65b60e1f73Sbluhm }
66b60e1f73Sbluhm 
67b60e1f73Sbluhm static void
68b60e1f73Sbluhm buffertls_readcb(int fd, short event, void *arg)
69b60e1f73Sbluhm {
70b60e1f73Sbluhm 	struct buffertls *buftls = arg;
71b60e1f73Sbluhm 	struct bufferevent *bufev = buftls->bt_bufev;
72b60e1f73Sbluhm 	struct tls *ctx = buftls->bt_ctx;
73b60e1f73Sbluhm 	int res = 0;
74b60e1f73Sbluhm 	short what = EVBUFFER_READ;
75b60e1f73Sbluhm 	size_t len;
76b60e1f73Sbluhm 	int howmuch = -1;
77b60e1f73Sbluhm 
78b60e1f73Sbluhm 	if (event == EV_TIMEOUT) {
79b60e1f73Sbluhm 		what |= EVBUFFER_TIMEOUT;
80b60e1f73Sbluhm 		goto error;
81b60e1f73Sbluhm 	}
82b60e1f73Sbluhm 
83b60e1f73Sbluhm 	/*
84b60e1f73Sbluhm 	 * If we have a high watermark configured then we don't want to
85b60e1f73Sbluhm 	 * read more data than would make us reach the watermark.
86b60e1f73Sbluhm 	 */
87b60e1f73Sbluhm 	if (bufev->wm_read.high != 0) {
88b60e1f73Sbluhm 		howmuch = bufev->wm_read.high - EVBUFFER_LENGTH(bufev->input);
89b60e1f73Sbluhm 		/* we might have lowered the watermark, stop reading */
90b60e1f73Sbluhm 		if (howmuch <= 0) {
91b60e1f73Sbluhm 			struct evbuffer *buf = bufev->input;
92b60e1f73Sbluhm 			event_del(&bufev->ev_read);
93b60e1f73Sbluhm 			evbuffer_setcb(buf,
94b60e1f73Sbluhm 			    bufferevent_read_pressure_cb, bufev);
95b60e1f73Sbluhm 			return;
96b60e1f73Sbluhm 		}
97b60e1f73Sbluhm 	}
98b60e1f73Sbluhm 
99b60e1f73Sbluhm 	res = evtls_read(bufev->input, fd, howmuch, ctx);
100b60e1f73Sbluhm 	switch (res) {
10165c6d236Sbluhm 	case TLS_WANT_POLLIN:
102f770f76fSbluhm 		bufferevent_add(&bufev->ev_read, bufev->timeout_read);
103f770f76fSbluhm 		return;
10465c6d236Sbluhm 	case TLS_WANT_POLLOUT:
105f770f76fSbluhm 		event_del(&bufev->ev_write);
106f770f76fSbluhm 		event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_readcb,
107b60e1f73Sbluhm 		    buftls);
108f770f76fSbluhm 		bufferevent_add(&bufev->ev_write, bufev->timeout_write);
109f770f76fSbluhm 		return;
110b60e1f73Sbluhm 	case -1:
111b60e1f73Sbluhm 		what |= EVBUFFER_ERROR;
112b60e1f73Sbluhm 		break;
113b60e1f73Sbluhm 	case 0:
1141d77af83Sbluhm 		tls_close(ctx);
115b60e1f73Sbluhm 		what |= EVBUFFER_EOF;
116b60e1f73Sbluhm 		break;
117b60e1f73Sbluhm 	}
118b60e1f73Sbluhm 	if (res <= 0)
119b60e1f73Sbluhm 		goto error;
120b60e1f73Sbluhm 
121f770f76fSbluhm 	event_del(&bufev->ev_write);
122f770f76fSbluhm 	event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls);
123f770f76fSbluhm 	if (bufev->enabled & EV_READ)
124b60e1f73Sbluhm 		bufferevent_add(&bufev->ev_read, bufev->timeout_read);
125f770f76fSbluhm 	if (EVBUFFER_LENGTH(bufev->output) != 0 && bufev->enabled & EV_WRITE)
126f770f76fSbluhm 		bufferevent_add(&bufev->ev_write, bufev->timeout_write);
127b60e1f73Sbluhm 
128b60e1f73Sbluhm 	/* See if this callbacks meets the water marks */
129b60e1f73Sbluhm 	len = EVBUFFER_LENGTH(bufev->input);
130b60e1f73Sbluhm 	if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
131b60e1f73Sbluhm 		return;
132b60e1f73Sbluhm 	if (bufev->wm_read.high != 0 && len >= bufev->wm_read.high) {
133b60e1f73Sbluhm 		struct evbuffer *buf = bufev->input;
134b60e1f73Sbluhm 		event_del(&bufev->ev_read);
135b60e1f73Sbluhm 
136b60e1f73Sbluhm 		/* Now schedule a callback for us when the buffer changes */
137b60e1f73Sbluhm 		evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);
138b60e1f73Sbluhm 	}
139b60e1f73Sbluhm 
140b60e1f73Sbluhm 	/* Invoke the user callback - must always be called last */
141b60e1f73Sbluhm 	if (bufev->readcb != NULL)
142b60e1f73Sbluhm 		(*bufev->readcb)(bufev, bufev->cbarg);
143b60e1f73Sbluhm 	return;
144b60e1f73Sbluhm 
145b60e1f73Sbluhm  error:
146b60e1f73Sbluhm 	(*bufev->errorcb)(bufev, what, bufev->cbarg);
147b60e1f73Sbluhm }
148b60e1f73Sbluhm 
149b60e1f73Sbluhm static void
150b60e1f73Sbluhm buffertls_writecb(int fd, short event, void *arg)
151b60e1f73Sbluhm {
152b60e1f73Sbluhm 	struct buffertls *buftls = arg;
153b60e1f73Sbluhm 	struct bufferevent *bufev = buftls->bt_bufev;
154b60e1f73Sbluhm 	struct tls *ctx = buftls->bt_ctx;
155b60e1f73Sbluhm 	int res = 0;
156b60e1f73Sbluhm 	short what = EVBUFFER_WRITE;
157b60e1f73Sbluhm 
158b60e1f73Sbluhm 	if (event == EV_TIMEOUT) {
159b60e1f73Sbluhm 		what |= EVBUFFER_TIMEOUT;
160b60e1f73Sbluhm 		goto error;
161b60e1f73Sbluhm 	}
162b60e1f73Sbluhm 
163b60e1f73Sbluhm 	if (EVBUFFER_LENGTH(bufev->output) != 0) {
164b60e1f73Sbluhm 		res = evtls_write(bufev->output, fd, ctx);
165b60e1f73Sbluhm 		switch (res) {
16665c6d236Sbluhm 		case TLS_WANT_POLLIN:
167f770f76fSbluhm 			event_del(&bufev->ev_read);
168f770f76fSbluhm 			event_set(&bufev->ev_read, fd, EV_READ,
169b60e1f73Sbluhm 			    buffertls_writecb, buftls);
170f770f76fSbluhm 			bufferevent_add(&bufev->ev_read, bufev->timeout_read);
171f770f76fSbluhm 			return;
17265c6d236Sbluhm 		case TLS_WANT_POLLOUT:
173f770f76fSbluhm 			bufferevent_add(&bufev->ev_write, bufev->timeout_write);
174f770f76fSbluhm 			return;
175b60e1f73Sbluhm 		case -1:
176b60e1f73Sbluhm 			what |= EVBUFFER_ERROR;
177b60e1f73Sbluhm 			break;
178b60e1f73Sbluhm 		case 0:
179b60e1f73Sbluhm 			what |= EVBUFFER_EOF;
180b60e1f73Sbluhm 			break;
181b60e1f73Sbluhm 		}
182b60e1f73Sbluhm 		if (res <= 0)
183b60e1f73Sbluhm 			goto error;
184b60e1f73Sbluhm 	}
185b60e1f73Sbluhm 
186f770f76fSbluhm 	event_del(&bufev->ev_read);
187f770f76fSbluhm 	event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, buftls);
188f770f76fSbluhm 	if (bufev->enabled & EV_READ)
189f770f76fSbluhm 		bufferevent_add(&bufev->ev_read, bufev->timeout_read);
190f770f76fSbluhm 	if (EVBUFFER_LENGTH(bufev->output) != 0 && bufev->enabled & EV_WRITE)
191b60e1f73Sbluhm 		bufferevent_add(&bufev->ev_write, bufev->timeout_write);
192b60e1f73Sbluhm 
193b60e1f73Sbluhm 	/*
194b60e1f73Sbluhm 	 * Invoke the user callback if our buffer is drained or below the
195b60e1f73Sbluhm 	 * low watermark.
196b60e1f73Sbluhm 	 */
197b60e1f73Sbluhm 	if (bufev->writecb != NULL &&
198b60e1f73Sbluhm 	    EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
199b60e1f73Sbluhm 		(*bufev->writecb)(bufev, bufev->cbarg);
200b60e1f73Sbluhm 
201b60e1f73Sbluhm 	return;
202b60e1f73Sbluhm 
203b60e1f73Sbluhm  error:
204b60e1f73Sbluhm 	(*bufev->errorcb)(bufev, what, bufev->cbarg);
205b60e1f73Sbluhm }
206b60e1f73Sbluhm 
207b60e1f73Sbluhm static void
208ab571fa1Sbluhm buffertls_handshakecb(int fd, short event, void *arg)
209b60e1f73Sbluhm {
210b60e1f73Sbluhm 	struct buffertls *buftls = arg;
211b60e1f73Sbluhm 	struct bufferevent *bufev = buftls->bt_bufev;
212b60e1f73Sbluhm 	struct tls *ctx = buftls->bt_ctx;
213b60e1f73Sbluhm 	int res = 0;
214ab571fa1Sbluhm 	short what = EVBUFFER_HANDSHAKE;
215b60e1f73Sbluhm 
216b60e1f73Sbluhm 	if (event == EV_TIMEOUT) {
217b60e1f73Sbluhm 		what |= EVBUFFER_TIMEOUT;
218b60e1f73Sbluhm 		goto error;
219b60e1f73Sbluhm 	}
220b60e1f73Sbluhm 
221ab571fa1Sbluhm 	res = tls_handshake(ctx);
222b60e1f73Sbluhm 	switch (res) {
22365c6d236Sbluhm 	case TLS_WANT_POLLIN:
224f770f76fSbluhm 		bufferevent_add(&bufev->ev_read, bufev->timeout_read);
225f770f76fSbluhm 		return;
22665c6d236Sbluhm 	case TLS_WANT_POLLOUT:
227f770f76fSbluhm 		bufferevent_add(&bufev->ev_write, bufev->timeout_write);
228f770f76fSbluhm 		return;
229b60e1f73Sbluhm 	case -1:
230b60e1f73Sbluhm 		what |= EVBUFFER_ERROR;
231b60e1f73Sbluhm 		break;
232b60e1f73Sbluhm 	}
233df69c215Sderaadt 	if (res == -1)
234b60e1f73Sbluhm 		goto error;
235b60e1f73Sbluhm 
236f770f76fSbluhm 	/* Handshake was successful, change to read and write callback. */
237f770f76fSbluhm 	event_del(&bufev->ev_read);
238f770f76fSbluhm 	event_del(&bufev->ev_write);
239f770f76fSbluhm 	event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, buftls);
240b60e1f73Sbluhm 	event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls);
241f770f76fSbluhm 	if (bufev->enabled & EV_READ)
242f770f76fSbluhm 		bufferevent_add(&bufev->ev_read, bufev->timeout_read);
243*18e6fc66Sbluhm 	if (bufev->enabled & EV_WRITE)
244b60e1f73Sbluhm 		bufferevent_add(&bufev->ev_write, bufev->timeout_write);
245b60e1f73Sbluhm 
246b60e1f73Sbluhm 	return;
247b60e1f73Sbluhm 
248b60e1f73Sbluhm  error:
249b60e1f73Sbluhm 	(*bufev->errorcb)(bufev, what, bufev->cbarg);
250b60e1f73Sbluhm }
251b60e1f73Sbluhm 
252b60e1f73Sbluhm void
253b60e1f73Sbluhm buffertls_set(struct buffertls *buftls, struct bufferevent *bufev,
254b60e1f73Sbluhm     struct tls *ctx, int fd)
255b60e1f73Sbluhm {
256b60e1f73Sbluhm 	bufferevent_setfd(bufev, fd);
257b60e1f73Sbluhm 	event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, buftls);
258b60e1f73Sbluhm 	event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls);
259b60e1f73Sbluhm 	buftls->bt_bufev = bufev;
260b60e1f73Sbluhm 	buftls->bt_ctx = ctx;
261b60e1f73Sbluhm }
262b60e1f73Sbluhm 
263b60e1f73Sbluhm void
2641cfd376aSbluhm buffertls_accept(struct buffertls *buftls, int fd)
2651cfd376aSbluhm {
2661cfd376aSbluhm 	struct bufferevent *bufev = buftls->bt_bufev;
2671cfd376aSbluhm 
2681cfd376aSbluhm 	event_del(&bufev->ev_read);
2691cfd376aSbluhm 	event_del(&bufev->ev_write);
2701cfd376aSbluhm 	event_set(&bufev->ev_read, fd, EV_READ, buffertls_handshakecb, buftls);
2711cfd376aSbluhm 	event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_handshakecb,
2721cfd376aSbluhm 	    buftls);
2731cfd376aSbluhm 	bufferevent_add(&bufev->ev_read, bufev->timeout_read);
2741cfd376aSbluhm }
2751cfd376aSbluhm 
2761cfd376aSbluhm void
277ab571fa1Sbluhm buffertls_connect(struct buffertls *buftls, int fd)
278b60e1f73Sbluhm {
279b60e1f73Sbluhm 	struct bufferevent *bufev = buftls->bt_bufev;
280b60e1f73Sbluhm 
281b60e1f73Sbluhm 	event_del(&bufev->ev_read);
282b60e1f73Sbluhm 	event_del(&bufev->ev_write);
283f770f76fSbluhm 	event_set(&bufev->ev_read, fd, EV_READ, buffertls_handshakecb, buftls);
284ab571fa1Sbluhm 	event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_handshakecb,
285ab571fa1Sbluhm 	    buftls);
2867c09be04Sbluhm 	bufferevent_add(&bufev->ev_write, bufev->timeout_write);
287b60e1f73Sbluhm }
288b60e1f73Sbluhm 
289b60e1f73Sbluhm /*
290b60e1f73Sbluhm  * Reads data from a file descriptor into a buffer.
291b60e1f73Sbluhm  */
292b60e1f73Sbluhm 
293e63efc4eSbluhm #define EVBUFFER_MAX_READ	16384
294b60e1f73Sbluhm 
295b60e1f73Sbluhm int
296b60e1f73Sbluhm evtls_read(struct evbuffer *buf, int fd, int howmuch, struct tls *ctx)
297b60e1f73Sbluhm {
298b60e1f73Sbluhm 	u_char *p;
29965c6d236Sbluhm 	size_t oldoff = buf->off;
300b60e1f73Sbluhm 	int n = EVBUFFER_MAX_READ;
301b60e1f73Sbluhm 
302b60e1f73Sbluhm 	if (howmuch < 0 || howmuch > n)
303b60e1f73Sbluhm 		howmuch = n;
304b60e1f73Sbluhm 
305b60e1f73Sbluhm 	/* If we don't have FIONREAD, we might waste some space here */
306b60e1f73Sbluhm 	if (evbuffer_expand(buf, howmuch) == -1)
307b60e1f73Sbluhm 		return (-1);
308b60e1f73Sbluhm 
309b60e1f73Sbluhm 	/* We can append new data at this point */
310b60e1f73Sbluhm 	p = buf->buffer + buf->off;
311b60e1f73Sbluhm 
31265c6d236Sbluhm 	n = tls_read(ctx, p, howmuch);
31365c6d236Sbluhm 	if (n <= 0)
314b60e1f73Sbluhm 		return (n);
315b60e1f73Sbluhm 
31665c6d236Sbluhm 	buf->off += n;
317b60e1f73Sbluhm 
318b60e1f73Sbluhm 	/* Tell someone about changes in this buffer */
319b60e1f73Sbluhm 	if (buf->off != oldoff && buf->cb != NULL)
320b60e1f73Sbluhm 		(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
321b60e1f73Sbluhm 
32265c6d236Sbluhm 	return (n);
323b60e1f73Sbluhm }
324b60e1f73Sbluhm 
325b60e1f73Sbluhm int
326b60e1f73Sbluhm evtls_write(struct evbuffer *buffer, int fd, struct tls *ctx)
327b60e1f73Sbluhm {
328b60e1f73Sbluhm 	int n;
329b60e1f73Sbluhm 
33065c6d236Sbluhm 	n = tls_write(ctx, buffer->buffer, buffer->off);
33165c6d236Sbluhm 	if (n <= 0)
332b60e1f73Sbluhm 		return (n);
33365c6d236Sbluhm 	evbuffer_drain(buffer, n);
334b60e1f73Sbluhm 
33565c6d236Sbluhm 	return (n);
336b60e1f73Sbluhm }
337