diff options
author | Joel Sing <jsing@cvs.openbsd.org> | 2015-02-07 04:33:52 +0000 |
---|---|---|
committer | Joel Sing <jsing@cvs.openbsd.org> | 2015-02-07 04:33:52 +0000 |
commit | 08ad1aaf4fa0c7e6e574ff78613994bf210cb7d6 (patch) | |
tree | 67462505c3116fafcb40be8801f23e3d731475f0 /lib | |
parent | 084d7988d49da5039c98224578ba885f4a4cae86 (diff) |
Attempt to implement the OpenSSL error dance so that TLS read/write
failures return something that is actually useful to the caller.
ok reyk@
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libtls/tls.c | 90 |
1 files changed, 61 insertions, 29 deletions
diff --git a/lib/libtls/tls.c b/lib/libtls/tls.c index 2ca53362607..696c35b4594 100644 --- a/lib/libtls/tls.c +++ b/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 <jsing@openbsd.org> * @@ -23,6 +23,7 @@ #include <unistd.h> #include <openssl/bio.h> +#include <openssl/err.h> #include <openssl/evp.h> #include <openssl/pem.h> #include <openssl/x509.h> @@ -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 |