diff options
| author | jsing <> | 2015-09-10 10:14:21 +0000 |
|---|---|---|
| committer | jsing <> | 2015-09-10 10:14:21 +0000 |
| commit | 801db6d0104e7b66f26bfc8d07ecb98601abc7ee (patch) | |
| tree | b1c4283700879b3793a5395cbab5ffd49e03f34f /src | |
| parent | 0dc5f09cfe416c2447e33a7ef2cbed7570956a15 (diff) | |
| download | openbsd-801db6d0104e7b66f26bfc8d07ecb98601abc7ee.tar.gz openbsd-801db6d0104e7b66f26bfc8d07ecb98601abc7ee.tar.bz2 openbsd-801db6d0104e7b66f26bfc8d07ecb98601abc7ee.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@
Diffstat (limited to '')
| -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 | } |
