xref: /openbsd-src/usr.sbin/relayd/ssl.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /*	$OpenBSD: ssl.c,v 1.15 2009/06/04 13:46:07 reyk Exp $	*/
2 
3 /*
4  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 
23 #include <net/if.h>
24 #include <netinet/in.h>
25 
26 #include <limits.h>
27 #include <event.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 
34 #include <openssl/ssl.h>
35 #include <openssl/err.h>
36 #include <openssl/engine.h>
37 
38 #include "relayd.h"
39 
40 void	ssl_read(int, short, void *);
41 void	ssl_write(int, short, void *);
42 void	ssl_connect(int, short, void *);
43 void	ssl_cleanup(struct ctl_tcp_event *);
44 
45 void
46 ssl_read(int s, short event, void *arg)
47 {
48 	struct ctl_tcp_event	*cte = arg;
49 	int			 ret;
50 	int			 ssl_err;
51 	int			 retry_flag;
52 	char			 rbuf[SMALL_READ_BUF_SIZE];
53 
54 	if (event == EV_TIMEOUT) {
55 		cte->host->up = HOST_DOWN;
56 		ssl_cleanup(cte);
57 		hce_notify_done(cte->host, HCE_SSL_READ_TIMEOUT);
58 		return;
59 	}
60 
61 	bzero(rbuf, sizeof(rbuf));
62 	ssl_err = 0;
63 	retry_flag = EV_READ;
64 
65 	ret = SSL_read(cte->ssl, rbuf, sizeof(rbuf));
66 	if (ret <= 0) {
67 		ssl_err = SSL_get_error(cte->ssl, ret);
68 		switch (ssl_err) {
69 		case SSL_ERROR_WANT_READ:
70 			retry_flag = EV_READ;
71 			goto retry;
72 		case SSL_ERROR_WANT_WRITE:
73 			retry_flag = EV_WRITE;
74 			goto retry;
75 		case SSL_ERROR_ZERO_RETURN: /* FALLTHROUGH */
76 		case SSL_ERROR_SYSCALL:
77 			if (ret == 0) {
78 				cte->host->up = HOST_DOWN;
79 				(void)cte->validate_close(cte);
80 				ssl_cleanup(cte);
81 				hce_notify_done(cte->host, cte->host->he);
82 				return;
83 			}
84 			/* FALLTHROUGH */
85 		default:
86 			cte->host->up = HOST_DOWN;
87 			ssl_error(cte->host->conf.name, "cannot read");
88 			ssl_cleanup(cte);
89 			hce_notify_done(cte->host, HCE_SSL_READ_ERROR);
90 			break;
91 		}
92 		return;
93 	}
94 	if (buf_add(cte->buf, rbuf, ret) == -1)
95 		fatal("ssl_read: buf_add error");
96 	if (cte->validate_read != NULL) {
97 		if (cte->validate_read(cte) != 0)
98 			goto retry;
99 
100 		ssl_cleanup(cte);
101 		hce_notify_done(cte->host, cte->host->he);
102 		return;
103 	}
104 
105 retry:
106 	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_read,
107 	    &cte->tv_start, &cte->table->conf.timeout, cte);
108 	return;
109 }
110 
111 void
112 ssl_write(int s, short event, void *arg)
113 {
114 	struct ctl_tcp_event	*cte = arg;
115 	int			 len;
116 	int			 ret;
117 	int			 ssl_err;
118 	int			 retry_flag;
119 
120 	if (event == EV_TIMEOUT) {
121 		cte->host->up = HOST_DOWN;
122 		ssl_cleanup(cte);
123 		hce_notify_done(cte->host, HCE_SSL_WRITE_TIMEOUT);
124 		return;
125 	}
126 
127 	len = strlen(cte->table->sendbuf);
128 	retry_flag = EV_WRITE;
129 
130 	ret = SSL_write(cte->ssl, cte->table->sendbuf, len);
131 	if (ret <= 0) {
132 		ssl_err = SSL_get_error(cte->ssl, ret);
133 		switch (ssl_err) {
134 		case SSL_ERROR_WANT_READ:
135 			retry_flag = EV_READ;
136 			goto retry;
137 		case SSL_ERROR_WANT_WRITE:
138 			retry_flag = EV_WRITE;
139 			goto retry;
140 		default:
141 			cte->host->up = HOST_DOWN;
142 			ssl_error(cte->host->conf.name, "cannot write");
143 			ssl_cleanup(cte);
144 			hce_notify_done(cte->host, HCE_SSL_WRITE_ERROR);
145 			return;
146 		}
147 	}
148 	if ((cte->buf = buf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL)
149 		fatalx("ssl_write: cannot create dynamic buffer");
150 
151 	event_again(&cte->ev, s, EV_TIMEOUT|EV_READ, ssl_read,
152 	    &cte->tv_start, &cte->table->conf.timeout, cte);
153 	return;
154 retry:
155 	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_write,
156 	    &cte->tv_start, &cte->table->conf.timeout, cte);
157 }
158 
159 void
160 ssl_connect(int s, short event, void *arg)
161 {
162 	struct ctl_tcp_event	*cte = arg;
163 	int			 ret;
164 	int			 ssl_err;
165 	int			 retry_flag;
166 
167 	if (event == EV_TIMEOUT) {
168 		cte->host->up = HOST_DOWN;
169 		hce_notify_done(cte->host, HCE_SSL_CONNECT_TIMEOUT);
170 		ssl_cleanup(cte);
171 		return;
172 	}
173 
174 	retry_flag = ssl_err = 0;
175 
176 	ret = SSL_connect(cte->ssl);
177 	if (ret <= 0) {
178 		ssl_err = SSL_get_error(cte->ssl, ret);
179 		switch (ssl_err) {
180 		case SSL_ERROR_WANT_READ:
181 			retry_flag = EV_READ;
182 			goto retry;
183 		case SSL_ERROR_WANT_WRITE:
184 			retry_flag = EV_WRITE;
185 			goto retry;
186 		default:
187 			cte->host->up = HOST_DOWN;
188 			ssl_error(cte->host->conf.name, "cannot connect");
189 			hce_notify_done(cte->host, HCE_SSL_CONNECT_FAIL);
190 			ssl_cleanup(cte);
191 			return;
192 		}
193 	}
194 
195 	if (cte->table->conf.check == CHECK_TCP) {
196 		cte->host->up = HOST_UP;
197 		hce_notify_done(cte->host, HCE_SSL_CONNECT_OK);
198 		ssl_cleanup(cte);
199 		return;
200 	}
201 	if (cte->table->sendbuf != NULL) {
202 		event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, ssl_write,
203 		    &cte->tv_start, &cte->table->conf.timeout, cte);
204 		return;
205 	}
206 
207 	if ((cte->buf = buf_dynamic(SMALL_READ_BUF_SIZE, UINT_MAX)) == NULL)
208 		fatalx("ssl_connect: cannot create dynamic buffer");
209 	event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_READ, ssl_read,
210 	    &cte->tv_start, &cte->table->conf.timeout, cte);
211 	return;
212 
213 retry:
214 	event_again(&cte->ev, s, EV_TIMEOUT|retry_flag, ssl_connect,
215 	    &cte->tv_start, &cte->table->conf.timeout, cte);
216 }
217 
218 void
219 ssl_cleanup(struct ctl_tcp_event *cte)
220 {
221 	close(cte->s);
222 	if (cte->ssl != NULL) {
223 		SSL_shutdown(cte->ssl);
224 		SSL_clear(cte->ssl);
225 	}
226 	if (cte->buf != NULL)
227 		buf_free(cte->buf);
228 }
229 
230 void
231 ssl_error(const char *where, const char *what)
232 {
233 	unsigned long	 code;
234 	char		 errbuf[128];
235 	extern int	 debug;
236 
237 	if (!debug)
238 		return;
239 	for (; (code = ERR_get_error()) != 0 ;) {
240 		ERR_error_string_n(code, errbuf, sizeof(errbuf));
241 		log_debug("SSL library error: %s: %s: %s", where, what, errbuf);
242 	}
243 }
244 
245 void
246 ssl_init(struct relayd *env)
247 {
248 	SSL_library_init();
249 	SSL_load_error_strings();
250 
251 	/* Init hardware crypto engines. */
252 	ENGINE_load_builtin_engines();
253 	ENGINE_register_all_complete();
254 }
255 
256 void
257 ssl_transaction(struct ctl_tcp_event *cte)
258 {
259 	if (cte->ssl == NULL) {
260 		cte->ssl = SSL_new(cte->table->ssl_ctx);
261 		if (cte->ssl == NULL) {
262 			ssl_error(cte->host->conf.name, "cannot create object");
263 			fatal("cannot create SSL object");
264 		}
265 	}
266 
267 	if (SSL_set_fd(cte->ssl, cte->s) == 0) {
268 		cte->host->up = HOST_UNKNOWN;
269 		ssl_error(cte->host->conf.name, "cannot set fd");
270 		ssl_cleanup(cte);
271 		hce_notify_done(cte->host, HCE_SSL_CONNECT_ERROR);
272 		return;
273 	}
274 	SSL_set_connect_state(cte->ssl);
275 
276 	event_again(&cte->ev, cte->s, EV_TIMEOUT|EV_WRITE, ssl_connect,
277 	    &cte->tv_start, &cte->table->conf.timeout, cte);
278 }
279 
280 SSL_CTX *
281 ssl_ctx_create(struct relayd *env)
282 {
283 	SSL_CTX	*ctx;
284 
285 	ctx = SSL_CTX_new(SSLv23_client_method());
286 	if (ctx == NULL) {
287 		ssl_error("ssl_ctx_create", "cannot create context");
288 		fatal("could not create SSL context");
289 	}
290 	return (ctx);
291 }
292