summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJoel Sing <jsing@cvs.openbsd.org>2015-02-07 04:33:52 +0000
committerJoel Sing <jsing@cvs.openbsd.org>2015-02-07 04:33:52 +0000
commit08ad1aaf4fa0c7e6e574ff78613994bf210cb7d6 (patch)
tree67462505c3116fafcb40be8801f23e3d731475f0 /lib
parent084d7988d49da5039c98224578ba885f4a4cae86 (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.c90
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