diff options
author | jsing <> | 2015-09-10 10:14:21 +0000 |
---|---|---|
committer | jsing <> | 2015-09-10 10:14:21 +0000 |
commit | f7415644a66ca9d04e5e06312a163e677032b695 (patch) | |
tree | b1c4283700879b3793a5395cbab5ffd49e03f34f | |
parent | fb8be3d22f2620af8ca6f69de96a1d4e5a8d153b (diff) | |
download | openbsd-f7415644a66ca9d04e5e06312a163e677032b695.tar.gz openbsd-f7415644a66ca9d04e5e06312a163e677032b695.tar.bz2 openbsd-f7415644a66ca9d04e5e06312a163e677032b695.zip |
Split tls_handshake() out from tls_accept/tls_connect. By doing this the
tls_accept/tls_connect functions can be guaranteed to succeed or fail and
will no longer return TLS_READ_AGAIN/TLS_WRITE_AGAIN. This also resolves
the semantics of tls_accept_*.
The tls_handshake() function now does I/O and can return
TLS_READ_AGAIN/TLS_WRITE_AGAIN. Calls to tls_read() and tls_write() will
trigger the handshake if it has not already completed, meaning that in many
cases existing code will continue to work.
Discussed over many coffees at l2k15.
ok beck@ bluhm@
-rw-r--r-- | src/lib/libtls/tls.c | 35 | ||||
-rw-r--r-- | src/lib/libtls/tls.h | 3 | ||||
-rw-r--r-- | src/lib/libtls/tls_client.c | 58 | ||||
-rw-r--r-- | src/lib/libtls/tls_internal.h | 7 | ||||
-rw-r--r-- | src/lib/libtls/tls_server.c | 76 |
5 files changed, 120 insertions, 59 deletions
diff --git a/src/lib/libtls/tls.c b/src/lib/libtls/tls.c index 6b9834565c..fe5bc964e2 100644 --- a/src/lib/libtls/tls.c +++ b/src/lib/libtls/tls.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls.c,v 1.19 2015/09/09 19:49:07 jsing Exp $ */ | 1 | /* $OpenBSD: tls.c,v 1.20 2015/09/10 10:14:20 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
4 | * | 4 | * |
@@ -315,6 +315,9 @@ tls_reset(struct tls *ctx) | |||
315 | ctx->socket = -1; | 315 | ctx->socket = -1; |
316 | ctx->state = 0; | 316 | ctx->state = 0; |
317 | 317 | ||
318 | free(ctx->servername); | ||
319 | ctx->servername = NULL; | ||
320 | |||
318 | free(ctx->errmsg); | 321 | free(ctx->errmsg); |
319 | ctx->errmsg = NULL; | 322 | ctx->errmsg = NULL; |
320 | ctx->errnum = 0; | 323 | ctx->errnum = 0; |
@@ -367,6 +370,20 @@ tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret, const char *prefix) | |||
367 | } | 370 | } |
368 | 371 | ||
369 | int | 372 | int |
373 | tls_handshake(struct tls *ctx) | ||
374 | { | ||
375 | int rv = -1; | ||
376 | |||
377 | if ((ctx->flags & TLS_CLIENT) != 0) | ||
378 | rv = tls_handshake_client(ctx); | ||
379 | else if ((ctx->flags & TLS_SERVER_CONN) != 0) | ||
380 | rv = tls_handshake_server(ctx); | ||
381 | |||
382 | errno = 0; | ||
383 | return (rv); | ||
384 | } | ||
385 | |||
386 | int | ||
370 | tls_read(struct tls *ctx, void *buf, size_t buflen, size_t *outlen) | 387 | tls_read(struct tls *ctx, void *buf, size_t buflen, size_t *outlen) |
371 | { | 388 | { |
372 | int ssl_ret; | 389 | int ssl_ret; |
@@ -374,13 +391,17 @@ tls_read(struct tls *ctx, void *buf, size_t buflen, size_t *outlen) | |||
374 | 391 | ||
375 | *outlen = 0; | 392 | *outlen = 0; |
376 | 393 | ||
394 | if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { | ||
395 | if ((rv = tls_handshake(ctx)) != 0) | ||
396 | goto out; | ||
397 | } | ||
398 | |||
377 | if (buflen > INT_MAX) { | 399 | if (buflen > INT_MAX) { |
378 | tls_set_errorx(ctx, "buflen too long"); | 400 | tls_set_errorx(ctx, "buflen too long"); |
379 | goto out; | 401 | goto out; |
380 | } | 402 | } |
381 | 403 | ||
382 | ssl_ret = SSL_read(ctx->ssl_conn, buf, buflen); | 404 | if ((ssl_ret = SSL_read(ctx->ssl_conn, buf, buflen)) > 0) { |
383 | if (ssl_ret > 0) { | ||
384 | *outlen = (size_t)ssl_ret; | 405 | *outlen = (size_t)ssl_ret; |
385 | rv = 0; | 406 | rv = 0; |
386 | goto out; | 407 | goto out; |
@@ -400,13 +421,17 @@ tls_write(struct tls *ctx, const void *buf, size_t buflen, size_t *outlen) | |||
400 | 421 | ||
401 | *outlen = 0; | 422 | *outlen = 0; |
402 | 423 | ||
424 | if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { | ||
425 | if ((rv = tls_handshake(ctx)) != 0) | ||
426 | goto out; | ||
427 | } | ||
428 | |||
403 | if (buflen > INT_MAX) { | 429 | if (buflen > INT_MAX) { |
404 | tls_set_errorx(ctx, "buflen too long"); | 430 | tls_set_errorx(ctx, "buflen too long"); |
405 | goto out; | 431 | goto out; |
406 | } | 432 | } |
407 | 433 | ||
408 | ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen); | 434 | if ((ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen)) > 0) { |
409 | if (ssl_ret > 0) { | ||
410 | *outlen = (size_t)ssl_ret; | 435 | *outlen = (size_t)ssl_ret; |
411 | rv = 0; | 436 | rv = 0; |
412 | goto out; | 437 | goto out; |
diff --git a/src/lib/libtls/tls.h b/src/lib/libtls/tls.h index 579a97798e..8548fe1d83 100644 --- a/src/lib/libtls/tls.h +++ b/src/lib/libtls/tls.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls.h,v 1.15 2015/09/10 09:10:42 jsing Exp $ */ | 1 | /* $OpenBSD: tls.h,v 1.16 2015/09/10 10:14:20 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
4 | * | 4 | * |
@@ -94,6 +94,7 @@ int tls_connect_fds(struct tls *_ctx, int _fd_read, int _fd_write, | |||
94 | int tls_connect_servername(struct tls *_ctx, const char *_host, | 94 | int tls_connect_servername(struct tls *_ctx, const char *_host, |
95 | const char *_port, const char *_servername); | 95 | const char *_port, const char *_servername); |
96 | int tls_connect_socket(struct tls *_ctx, int _s, const char *_servername); | 96 | int tls_connect_socket(struct tls *_ctx, int _s, const char *_servername); |
97 | int tls_handshake(struct tls *_ctx); | ||
97 | int tls_read(struct tls *_ctx, void *_buf, size_t _buflen, size_t *_outlen); | 98 | int tls_read(struct tls *_ctx, void *_buf, size_t _buflen, size_t *_outlen); |
98 | int tls_write(struct tls *_ctx, const void *_buf, size_t _buflen, | 99 | int tls_write(struct tls *_ctx, const void *_buf, size_t _buflen, |
99 | size_t *_outlen); | 100 | size_t *_outlen); |
diff --git a/src/lib/libtls/tls_client.c b/src/lib/libtls/tls_client.c index 81e47da0e1..fb7f3a6f75 100644 --- a/src/lib/libtls/tls_client.c +++ b/src/lib/libtls/tls_client.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_client.c,v 1.25 2015/09/09 19:49:07 jsing Exp $ */ | 1 | /* $OpenBSD: tls_client.c,v 1.26 2015/09/10 10:14:20 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
4 | * | 4 | * |
@@ -166,20 +166,23 @@ tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, | |||
166 | const char *servername) | 166 | const char *servername) |
167 | { | 167 | { |
168 | union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; | 168 | union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; |
169 | X509 *cert = NULL; | 169 | int rv = -1; |
170 | int ret, err; | ||
171 | 170 | ||
172 | if ((ctx->flags & TLS_CLIENT) == 0) { | 171 | if ((ctx->flags & TLS_CLIENT) == 0) { |
173 | tls_set_errorx(ctx, "not a client context"); | 172 | tls_set_errorx(ctx, "not a client context"); |
174 | goto err; | 173 | goto err; |
175 | } | 174 | } |
176 | 175 | ||
177 | if (ctx->state & TLS_STATE_CONNECTING) | ||
178 | goto connecting; | ||
179 | |||
180 | if (fd_read < 0 || fd_write < 0) { | 176 | if (fd_read < 0 || fd_write < 0) { |
181 | tls_set_errorx(ctx, "invalid file descriptors"); | 177 | tls_set_errorx(ctx, "invalid file descriptors"); |
182 | return (-1); | 178 | goto err; |
179 | } | ||
180 | |||
181 | if (servername != NULL) { | ||
182 | if ((ctx->servername = strdup(servername)) == NULL) { | ||
183 | tls_set_errorx(ctx, "out of memory"); | ||
184 | goto err; | ||
185 | } | ||
183 | } | 186 | } |
184 | 187 | ||
185 | if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { | 188 | if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { |
@@ -230,16 +233,28 @@ tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, | |||
230 | } | 233 | } |
231 | } | 234 | } |
232 | 235 | ||
233 | connecting: | 236 | rv = 0; |
234 | if ((ret = SSL_connect(ctx->ssl_conn)) != 1) { | 237 | |
235 | err = tls_ssl_error(ctx, ctx->ssl_conn, ret, "connect"); | 238 | err: |
236 | if (err == TLS_READ_AGAIN || err == TLS_WRITE_AGAIN) { | 239 | return (rv); |
237 | ctx->state |= TLS_STATE_CONNECTING; | 240 | } |
238 | return (err); | 241 | |
239 | } | 242 | int |
243 | tls_handshake_client(struct tls *ctx) | ||
244 | { | ||
245 | X509 *cert = NULL; | ||
246 | int ssl_ret; | ||
247 | int rv = -1; | ||
248 | |||
249 | if ((ctx->flags & TLS_CLIENT) == 0) { | ||
250 | tls_set_errorx(ctx, "not a client context"); | ||
251 | goto err; | ||
252 | } | ||
253 | |||
254 | if ((ssl_ret = SSL_connect(ctx->ssl_conn)) != 1) { | ||
255 | rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); | ||
240 | goto err; | 256 | goto err; |
241 | } | 257 | } |
242 | ctx->state &= ~TLS_STATE_CONNECTING; | ||
243 | 258 | ||
244 | if (ctx->config->verify_name) { | 259 | if (ctx->config->verify_name) { |
245 | cert = SSL_get_peer_certificate(ctx->ssl_conn); | 260 | cert = SSL_get_peer_certificate(ctx->ssl_conn); |
@@ -247,19 +262,20 @@ tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, | |||
247 | tls_set_errorx(ctx, "no server certificate"); | 262 | tls_set_errorx(ctx, "no server certificate"); |
248 | goto err; | 263 | goto err; |
249 | } | 264 | } |
250 | if ((ret = tls_check_servername(ctx, cert, servername)) != 0) { | 265 | if ((rv = tls_check_servername(ctx, cert, |
251 | if (ret != -2) | 266 | ctx->servername)) != 0) { |
267 | if (rv != -2) | ||
252 | tls_set_errorx(ctx, "name `%s' not present in" | 268 | tls_set_errorx(ctx, "name `%s' not present in" |
253 | " server certificate", servername); | 269 | " server certificate", ctx->servername); |
254 | goto err; | 270 | goto err; |
255 | } | 271 | } |
256 | X509_free(cert); | ||
257 | } | 272 | } |
258 | 273 | ||
259 | return (0); | 274 | ctx->state |= TLS_HANDSHAKE_COMPLETE; |
275 | rv = 0; | ||
260 | 276 | ||
261 | err: | 277 | err: |
262 | X509_free(cert); | 278 | X509_free(cert); |
263 | 279 | ||
264 | return (-1); | 280 | return (rv); |
265 | } | 281 | } |
diff --git a/src/lib/libtls/tls_internal.h b/src/lib/libtls/tls_internal.h index 78ae542cb6..a5399d5594 100644 --- a/src/lib/libtls/tls_internal.h +++ b/src/lib/libtls/tls_internal.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_internal.h,v 1.17 2015/09/10 09:10:42 jsing Exp $ */ | 1 | /* $OpenBSD: tls_internal.h,v 1.18 2015/09/10 10:14:20 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> | 3 | * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> |
4 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 4 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
@@ -52,7 +52,7 @@ struct tls_config { | |||
52 | #define TLS_SERVER (1 << 1) | 52 | #define TLS_SERVER (1 << 1) |
53 | #define TLS_SERVER_CONN (1 << 2) | 53 | #define TLS_SERVER_CONN (1 << 2) |
54 | 54 | ||
55 | #define TLS_STATE_CONNECTING (1 << 0) | 55 | #define TLS_HANDSHAKE_COMPLETE (1 << 0) |
56 | 56 | ||
57 | struct tls { | 57 | struct tls { |
58 | struct tls_config *config; | 58 | struct tls_config *config; |
@@ -62,6 +62,7 @@ struct tls { | |||
62 | char *errmsg; | 62 | char *errmsg; |
63 | int errnum; | 63 | int errnum; |
64 | 64 | ||
65 | char *servername; | ||
65 | int socket; | 66 | int socket; |
66 | 67 | ||
67 | SSL *ssl_conn; | 68 | SSL *ssl_conn; |
@@ -76,6 +77,8 @@ int tls_configure_keypair(struct tls *ctx, int); | |||
76 | int tls_configure_server(struct tls *ctx); | 77 | int tls_configure_server(struct tls *ctx); |
77 | int tls_configure_ssl(struct tls *ctx); | 78 | int tls_configure_ssl(struct tls *ctx); |
78 | int tls_configure_ssl_verify(struct tls *ctx, int verify); | 79 | int tls_configure_ssl_verify(struct tls *ctx, int verify); |
80 | int tls_handshake_client(struct tls *ctx); | ||
81 | int tls_handshake_server(struct tls *ctx); | ||
79 | int tls_host_port(const char *hostport, char **host, char **port); | 82 | int tls_host_port(const char *hostport, char **host, char **port); |
80 | int tls_set_error(struct tls *ctx, const char *fmt, ...) | 83 | int tls_set_error(struct tls *ctx, const char *fmt, ...) |
81 | __attribute__((__format__ (printf, 2, 3))) | 84 | __attribute__((__format__ (printf, 2, 3))) |
diff --git a/src/lib/libtls/tls_server.c b/src/lib/libtls/tls_server.c index a3cee09596..3dfd29ac19 100644 --- a/src/lib/libtls/tls_server.c +++ b/src/lib/libtls/tls_server.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_server.c,v 1.14 2015/09/10 09:10:42 jsing Exp $ */ | 1 | /* $OpenBSD: tls_server.c,v 1.15 2015/09/10 10:14:20 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
4 | * | 4 | * |
@@ -110,54 +110,70 @@ tls_configure_server(struct tls *ctx) | |||
110 | } | 110 | } |
111 | 111 | ||
112 | int | 112 | int |
113 | tls_accept_socket(struct tls *ctx, struct tls **cctx, int socket) | ||
114 | { | ||
115 | return (tls_accept_fds(ctx, cctx, socket, socket)); | ||
116 | } | ||
117 | |||
118 | int | ||
113 | tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write) | 119 | tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write) |
114 | { | 120 | { |
115 | struct tls *conn_ctx = *cctx; | 121 | struct tls *conn_ctx = NULL; |
116 | int ret, err; | ||
117 | 122 | ||
118 | if ((ctx->flags & TLS_SERVER) == 0) { | 123 | if ((ctx->flags & TLS_SERVER) == 0) { |
119 | tls_set_errorx(ctx, "not a server context"); | 124 | tls_set_errorx(ctx, "not a server context"); |
120 | goto err; | 125 | goto err; |
121 | } | 126 | } |
122 | 127 | ||
123 | if (conn_ctx == NULL) { | 128 | if ((conn_ctx = tls_server_conn(ctx)) == NULL) { |
124 | if ((conn_ctx = tls_server_conn(ctx)) == NULL) { | 129 | tls_set_errorx(ctx, "connection context failure"); |
125 | tls_set_errorx(ctx, "connection context failure"); | 130 | goto err; |
126 | goto err; | ||
127 | } | ||
128 | *cctx = conn_ctx; | ||
129 | |||
130 | if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { | ||
131 | tls_set_errorx(ctx, "ssl failure"); | ||
132 | goto err; | ||
133 | } | ||
134 | if (SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx) != 1) { | ||
135 | tls_set_errorx(ctx, "ssl application data failure"); | ||
136 | goto err; | ||
137 | } | ||
138 | if (SSL_set_rfd(conn_ctx->ssl_conn, fd_read) != 1 || | ||
139 | SSL_set_wfd(conn_ctx->ssl_conn, fd_write) != 1) { | ||
140 | tls_set_errorx(ctx, "ssl file descriptor failure"); | ||
141 | goto err; | ||
142 | } | ||
143 | } | 131 | } |
144 | 132 | ||
145 | if ((ret = SSL_accept(conn_ctx->ssl_conn)) != 1) { | 133 | if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { |
146 | err = tls_ssl_error(ctx, conn_ctx->ssl_conn, ret, "accept"); | 134 | tls_set_errorx(ctx, "ssl failure"); |
147 | if (err == TLS_READ_AGAIN || err == TLS_WRITE_AGAIN) { | 135 | goto err; |
148 | return (err); | 136 | } |
149 | } | 137 | if (SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx) != 1) { |
138 | tls_set_errorx(ctx, "ssl application data failure"); | ||
139 | goto err; | ||
140 | } | ||
141 | if (SSL_set_rfd(conn_ctx->ssl_conn, fd_read) != 1 || | ||
142 | SSL_set_wfd(conn_ctx->ssl_conn, fd_write) != 1) { | ||
143 | tls_set_errorx(ctx, "ssl file descriptor failure"); | ||
150 | goto err; | 144 | goto err; |
151 | } | 145 | } |
152 | 146 | ||
147 | *cctx = conn_ctx; | ||
148 | |||
153 | return (0); | 149 | return (0); |
154 | 150 | ||
155 | err: | 151 | err: |
152 | tls_free(conn_ctx); | ||
153 | |||
154 | *cctx = NULL; | ||
155 | |||
156 | return (-1); | 156 | return (-1); |
157 | } | 157 | } |
158 | 158 | ||
159 | int | 159 | int |
160 | tls_accept_socket(struct tls *ctx, struct tls **cctx, int socket) | 160 | tls_handshake_server(struct tls *ctx) |
161 | { | 161 | { |
162 | return (tls_accept_fds(ctx, cctx, socket, socket)); | 162 | int ssl_ret; |
163 | int rv = -1; | ||
164 | |||
165 | if ((ctx->flags & TLS_SERVER_CONN) == 0) { | ||
166 | tls_set_errorx(ctx, "not a server connection context"); | ||
167 | goto err; | ||
168 | } | ||
169 | |||
170 | if ((ssl_ret = SSL_accept(ctx->ssl_conn)) != 1) { | ||
171 | rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); | ||
172 | goto err; | ||
173 | } | ||
174 | |||
175 | ctx->state |= TLS_HANDSHAKE_COMPLETE; | ||
176 | |||
177 | err: | ||
178 | return (rv); | ||
163 | } | 179 | } |