From be7bacd2cbd3fc2572c636ac04282b2ca64f7272 Mon Sep 17 00:00:00 2001 From: jsing <> Date: Sat, 7 Feb 2015 04:33:51 +0000 Subject: Attempt to implement the OpenSSL error dance so that TLS read/write failures return something that is actually useful to the caller. ok reyk@ --- src/lib/libtls/tls.c | 90 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/lib/libtls/tls.c b/src/lib/libtls/tls.c index 2ca5336260..696c35b459 100644 --- a/src/lib/libtls/tls.c +++ b/src/lib/libtls/tls.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tls.c,v 1.5 2015/02/06 01:37:11 reyk Exp $ */ +/* $OpenBSD: tls.c,v 1.6 2015/02/07 04:33:51 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -235,60 +236,91 @@ tls_reset(struct tls *ctx) ctx->errmsg = NULL; } -int -tls_read(struct tls *ctx, void *buf, size_t buflen, size_t *outlen) +static int +tls_ssl_error(struct tls *ctx, int ssl_ret, const char *prefix) { - int ret, ssl_err; - - if (buflen > INT_MAX) { - tls_set_error(ctx, "buflen too long"); - return (-1); - } + const char *errstr = "unknown error"; + unsigned long err; + int ssl_err; - ret = SSL_read(ctx->ssl_conn, buf, buflen); - if (ret > 0) { - *outlen = (size_t)ret; + ssl_err = SSL_get_error(ctx->ssl_conn, ssl_ret); + switch (ssl_err) { + case SSL_ERROR_NONE: return (0); - } - ssl_err = SSL_get_error(ctx->ssl_conn, ret); - switch (ssl_err) { + case SSL_ERROR_ZERO_RETURN: + tls_set_error(ctx, "%s failed: TLS connection closed", prefix); + return (-1); + case SSL_ERROR_WANT_READ: return (TLS_READ_AGAIN); + case SSL_ERROR_WANT_WRITE: return (TLS_WRITE_AGAIN); + + case SSL_ERROR_SYSCALL: + if ((err = ERR_peek_error()) != 0) { + errstr = ERR_error_string(err, NULL); + } else if (ssl_ret == 0) { + errstr = "EOF"; + } else if (ssl_ret == -1) { + errstr = strerror(errno); + } + tls_set_error(ctx, "%s failed: %s", prefix, errstr); + return (-1); + + case SSL_ERROR_SSL: + if ((err = ERR_peek_error()) != 0) { + errstr = ERR_error_string(err, NULL); + } + tls_set_error(ctx, "%s failed: %s", prefix, errstr); + return (-1); + + case SSL_ERROR_WANT_CONNECT: + case SSL_ERROR_WANT_ACCEPT: + case SSL_ERROR_WANT_X509_LOOKUP: default: - tls_set_error(ctx, "read failed (%i)", ssl_err); + tls_set_error(ctx, "%s failed (%i)", prefix, ssl_err); return (-1); } } int -tls_write(struct tls *ctx, const void *buf, size_t buflen, size_t *outlen) +tls_read(struct tls *ctx, void *buf, size_t buflen, size_t *outlen) { - int ret, ssl_err; + int ssl_ret; if (buflen > INT_MAX) { tls_set_error(ctx, "buflen too long"); return (-1); } - ret = SSL_write(ctx->ssl_conn, buf, buflen); - if (ret > 0) { - *outlen = (size_t)ret; + ssl_ret = SSL_read(ctx->ssl_conn, buf, buflen); + if (ssl_ret > 0) { + *outlen = (size_t)ssl_ret; return (0); } - ssl_err = SSL_get_error(ctx->ssl_conn, ret); - switch (ssl_err) { - case SSL_ERROR_WANT_READ: - return (TLS_READ_AGAIN); - case SSL_ERROR_WANT_WRITE: - return (TLS_WRITE_AGAIN); - default: - tls_set_error(ctx, "write failed (%i)", ssl_err); + return tls_ssl_error(ctx, ssl_ret, "read"); +} + +int +tls_write(struct tls *ctx, const void *buf, size_t buflen, size_t *outlen) +{ + int ssl_ret; + + if (buflen > INT_MAX) { + tls_set_error(ctx, "buflen too long"); return (-1); } + + ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen); + if (ssl_ret > 0) { + *outlen = (size_t)ssl_ret; + return (0); + } + + return tls_ssl_error(ctx, ssl_ret, "write"); } int -- cgit v1.2.3-55-g6feb