xref: /openbsd-src/usr.sbin/syslogd/evbuffer_tls.c (revision b60e1f73b6566ceae714960a532385331d2fc9e5)
1 /*	$OpenBSD: evbuffer_tls.c,v 1.1 2015/01/18 19:37:59 bluhm Exp $ */
2 
3 /*
4  * Copyright (c) 2002-2004 Niels Provos <provos@citi.umich.edu>
5  * Copyright (c) 2014-2015 Alexander Bluhm <bluhm@openbsd.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/time.h>
33 #include <sys/ioctl.h>
34 
35 #include <errno.h>
36 #include <event.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <stdarg.h>
41 #include <tls.h>
42 
43 #include "evbuffer_tls.h"
44 
45 /* prototypes */
46 
47 void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *);
48 int evtls_read(struct evbuffer *, int, int, struct tls *);
49 int evtls_write(struct evbuffer *, int, struct tls *);
50 
51 static int
52 bufferevent_add(struct event *ev, int timeout)
53 {
54 	struct timeval tv, *ptv = NULL;
55 
56 	if (timeout) {
57 		timerclear(&tv);
58 		tv.tv_sec = timeout;
59 		ptv = &tv;
60 	}
61 
62 	return (event_add(ev, ptv));
63 }
64 
65 static void
66 buffertls_readcb(int fd, short event, void *arg)
67 {
68 	struct buffertls *buftls = arg;
69 	struct bufferevent *bufev = buftls->bt_bufev;
70 	struct tls *ctx = buftls->bt_ctx;
71 	int res = 0;
72 	short what = EVBUFFER_READ;
73 	size_t len;
74 	int howmuch = -1;
75 
76 	if (event == EV_TIMEOUT) {
77 		what |= EVBUFFER_TIMEOUT;
78 		goto error;
79 	}
80 
81 	/*
82 	 * If we have a high watermark configured then we don't want to
83 	 * read more data than would make us reach the watermark.
84 	 */
85 	if (bufev->wm_read.high != 0) {
86 		howmuch = bufev->wm_read.high - EVBUFFER_LENGTH(bufev->input);
87 		/* we might have lowered the watermark, stop reading */
88 		if (howmuch <= 0) {
89 			struct evbuffer *buf = bufev->input;
90 			event_del(&bufev->ev_read);
91 			evbuffer_setcb(buf,
92 			    bufferevent_read_pressure_cb, bufev);
93 			return;
94 		}
95 	}
96 
97 	res = evtls_read(bufev->input, fd, howmuch, ctx);
98 	switch (res) {
99 	case TLS_READ_AGAIN:
100 		event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb,
101 		    buftls);
102 		goto reschedule;
103 	case TLS_WRITE_AGAIN:
104 		event_set(&bufev->ev_read, fd, EV_WRITE, buffertls_readcb,
105 		    buftls);
106 		goto reschedule;
107 	case -1:
108 		if (errno == EAGAIN || errno == EINTR)
109 			goto reschedule;
110 		/* error case */
111 		what |= EVBUFFER_ERROR;
112 		break;
113 	case 0:
114 		/* eof case */
115 		what |= EVBUFFER_EOF;
116 		break;
117 	}
118 	if (res <= 0)
119 		goto error;
120 
121 	event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, buftls);
122 	bufferevent_add(&bufev->ev_read, bufev->timeout_read);
123 
124 	/* See if this callbacks meets the water marks */
125 	len = EVBUFFER_LENGTH(bufev->input);
126 	if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
127 		return;
128 	if (bufev->wm_read.high != 0 && len >= bufev->wm_read.high) {
129 		struct evbuffer *buf = bufev->input;
130 		event_del(&bufev->ev_read);
131 
132 		/* Now schedule a callback for us when the buffer changes */
133 		evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);
134 	}
135 
136 	/* Invoke the user callback - must always be called last */
137 	if (bufev->readcb != NULL)
138 		(*bufev->readcb)(bufev, bufev->cbarg);
139 	return;
140 
141  reschedule:
142 	bufferevent_add(&bufev->ev_read, bufev->timeout_read);
143 	return;
144 
145  error:
146 	(*bufev->errorcb)(bufev, what, bufev->cbarg);
147 }
148 
149 static void
150 buffertls_writecb(int fd, short event, void *arg)
151 {
152 	struct buffertls *buftls = arg;
153 	struct bufferevent *bufev = buftls->bt_bufev;
154 	struct tls *ctx = buftls->bt_ctx;
155 	int res = 0;
156 	short what = EVBUFFER_WRITE;
157 
158 	if (event == EV_TIMEOUT) {
159 		what |= EVBUFFER_TIMEOUT;
160 		goto error;
161 	}
162 
163 	if (EVBUFFER_LENGTH(bufev->output) != 0) {
164 		res = evtls_write(bufev->output, fd, ctx);
165 		switch (res) {
166 		case TLS_READ_AGAIN:
167 			event_set(&bufev->ev_write, fd, EV_READ,
168 			    buffertls_writecb, buftls);
169 			goto reschedule;
170 		case TLS_WRITE_AGAIN:
171 			event_set(&bufev->ev_write, fd, EV_WRITE,
172 			    buffertls_writecb, buftls);
173 			goto reschedule;
174 		case -1:
175 			if (errno == EAGAIN || errno == EINTR ||
176 			    errno == EINPROGRESS)
177 				goto reschedule;
178 			/* error case */
179 			what |= EVBUFFER_ERROR;
180 			break;
181 		case 0:
182 			/* eof case */
183 			what |= EVBUFFER_EOF;
184 			break;
185 		}
186 		if (res <= 0)
187 			goto error;
188 	}
189 
190 	event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls);
191 	if (EVBUFFER_LENGTH(bufev->output) != 0)
192 		bufferevent_add(&bufev->ev_write, bufev->timeout_write);
193 
194 	/*
195 	 * Invoke the user callback if our buffer is drained or below the
196 	 * low watermark.
197 	 */
198 	if (bufev->writecb != NULL &&
199 	    EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
200 		(*bufev->writecb)(bufev, bufev->cbarg);
201 
202 	return;
203 
204  reschedule:
205 	if (EVBUFFER_LENGTH(bufev->output) != 0)
206 		bufferevent_add(&bufev->ev_write, bufev->timeout_write);
207 	return;
208 
209  error:
210 	(*bufev->errorcb)(bufev, what, bufev->cbarg);
211 }
212 
213 static void
214 buffertls_connectcb(int fd, short event, void *arg)
215 {
216 	struct buffertls *buftls = arg;
217 	struct bufferevent *bufev = buftls->bt_bufev;
218 	struct tls *ctx = buftls->bt_ctx;
219 	const char *hostname = buftls->bt_hostname;
220 	int res = 0;
221 	short what = EVBUFFER_CONNECT;
222 
223 	if (event == EV_TIMEOUT) {
224 		what |= EVBUFFER_TIMEOUT;
225 		goto error;
226 	}
227 
228 	res = tls_connect_socket(ctx, fd, hostname);
229 	switch (res) {
230 	case TLS_READ_AGAIN:
231 		event_set(&bufev->ev_write, fd, EV_READ,
232 		    buffertls_connectcb, buftls);
233 		goto reschedule;
234 	case TLS_WRITE_AGAIN:
235 		event_set(&bufev->ev_write, fd, EV_WRITE,
236 		    buffertls_connectcb, buftls);
237 		goto reschedule;
238 	case -1:
239 		if (errno == EAGAIN || errno == EINTR ||
240 		    errno == EINPROGRESS)
241 			goto reschedule;
242 		/* error case */
243 		what |= EVBUFFER_ERROR;
244 		break;
245 	}
246 	if (res < 0)
247 		goto error;
248 
249 	/*
250 	 * There might be data available in the tls layer.  Try
251 	 * an read operation and setup the callbacks.  Call the read
252 	 * callback after enabling the write callback to give the
253 	 * read error handler a chance to disable the write event.
254 	 */
255 	event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls);
256 	if (EVBUFFER_LENGTH(bufev->output) != 0)
257 		bufferevent_add(&bufev->ev_write, bufev->timeout_write);
258 	buffertls_readcb(fd, 0, buftls);
259 
260 	return;
261 
262  reschedule:
263 	bufferevent_add(&bufev->ev_write, bufev->timeout_write);
264 	return;
265 
266  error:
267 	(*bufev->errorcb)(bufev, what, bufev->cbarg);
268 }
269 
270 void
271 buffertls_set(struct buffertls *buftls, struct bufferevent *bufev,
272     struct tls *ctx, int fd)
273 {
274 	bufferevent_setfd(bufev, fd);
275 	event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, buftls);
276 	event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls);
277 	buftls->bt_bufev = bufev;
278 	buftls->bt_ctx = ctx;
279 }
280 
281 void
282 buffertls_connect(struct buffertls *buftls, int fd, const char *hostname)
283 {
284 	struct bufferevent *bufev = buftls->bt_bufev;
285 
286 	event_del(&bufev->ev_read);
287 	event_del(&bufev->ev_write);
288 
289 	buftls->bt_hostname = hostname;
290 	buffertls_connectcb(fd, 0, buftls);
291 }
292 
293 /*
294  * Reads data from a file descriptor into a buffer.
295  */
296 
297 #define EVBUFFER_MAX_READ	4096
298 
299 int
300 evtls_read(struct evbuffer *buf, int fd, int howmuch, struct tls *ctx)
301 {
302 	u_char *p;
303 	size_t len, oldoff = buf->off;
304 	int n = EVBUFFER_MAX_READ;
305 
306 	if (ioctl(fd, FIONREAD, &n) == -1 || n <= 0) {
307 		n = EVBUFFER_MAX_READ;
308 	} else if (n > EVBUFFER_MAX_READ && n > howmuch) {
309 		/*
310 		 * It's possible that a lot of data is available for
311 		 * reading.  We do not want to exhaust resources
312 		 * before the reader has a chance to do something
313 		 * about it.  If the reader does not tell us how much
314 		 * data we should read, we artifically limit it.
315 		 */
316 		if ((size_t)n > buf->totallen << 2)
317 			n = buf->totallen << 2;
318 		if (n < EVBUFFER_MAX_READ)
319 			n = EVBUFFER_MAX_READ;
320 	}
321 	if (howmuch < 0 || howmuch > n)
322 		howmuch = n;
323 
324 	/* If we don't have FIONREAD, we might waste some space here */
325 	if (evbuffer_expand(buf, howmuch) == -1)
326 		return (-1);
327 
328 	/* We can append new data at this point */
329 	p = buf->buffer + buf->off;
330 
331 	n = tls_read(ctx, p, howmuch, &len);
332 	if (n < 0 || len == 0)
333 		return (n);
334 
335 	buf->off += len;
336 
337 	/* Tell someone about changes in this buffer */
338 	if (buf->off != oldoff && buf->cb != NULL)
339 		(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
340 
341 	return (len);
342 }
343 
344 int
345 evtls_write(struct evbuffer *buffer, int fd, struct tls *ctx)
346 {
347 	size_t len;
348 	int n;
349 
350 	n = tls_write(ctx, buffer->buffer, buffer->off, &len);
351 	if (n < 0 || len == 0)
352 		return (n);
353 
354 	evbuffer_drain(buffer, len);
355 
356 	return (len);
357 }
358