diff options
author | Daniel P. Berrangé <berrange@redhat.com> | 2024-03-15 14:29:11 +0000 |
---|---|---|
committer | Daniel P. Berrangé <berrange@redhat.com> | 2024-07-24 10:39:10 +0100 |
commit | 97f7bf113eb50fcdaf0c73aa2ee01e5355abc073 (patch) | |
tree | 2efc788f71855eae6b5aebf68ead7e0a36c2cfcf /crypto | |
parent | 57941c9c86357a6a642f9ee3279d881df4043b6d (diff) |
crypto: propagate errors from TLS session I/O callbacks
GNUTLS doesn't know how to perform I/O on anything other than plain
FDs, so the TLS session provides it with some I/O callbacks. The
GNUTLS API design requires these callbacks to return a unix errno
value, which means we're currently loosing the useful QEMU "Error"
object.
This changes the I/O callbacks in QEMU to stash the "Error" object
in the QCryptoTLSSession class, and fetch it when seeing an I/O
error returned from GNUTLS, thus preserving useful error messages.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/tlssession.c | 76 |
1 files changed, 66 insertions, 10 deletions
diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 926f19c115..77286e23f4 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -44,6 +44,13 @@ struct QCryptoTLSSession { QCryptoTLSSessionReadFunc readFunc; void *opaque; char *peername; + + /* + * Allow concurrent reads and writes, so track + * errors separately + */ + Error *rerr; + Error *werr; }; @@ -54,6 +61,9 @@ qcrypto_tls_session_free(QCryptoTLSSession *session) return; } + error_free(session->rerr); + error_free(session->werr); + gnutls_deinit(session->handle); g_free(session->hostname); g_free(session->peername); @@ -67,13 +77,26 @@ static ssize_t qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) { QCryptoTLSSession *session = opaque; + ssize_t ret; if (!session->writeFunc) { errno = EIO; return -1; }; - return session->writeFunc(buf, len, session->opaque); + error_free(session->werr); + session->werr = NULL; + + ret = session->writeFunc(buf, len, session->opaque, &session->werr); + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { + errno = EAGAIN; + return -1; + } else if (ret < 0) { + errno = EIO; + return -1; + } else { + return ret; + } } @@ -81,13 +104,26 @@ static ssize_t qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) { QCryptoTLSSession *session = opaque; + ssize_t ret; if (!session->readFunc) { errno = EIO; return -1; }; - return session->readFunc(buf, len, session->opaque); + error_free(session->rerr); + session->rerr = NULL; + + ret = session->readFunc(buf, len, session->opaque, &session->rerr); + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { + errno = EAGAIN; + return -1; + } else if (ret < 0) { + errno = EIO; + return -1; + } else { + return ret; + } } #define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH" @@ -450,9 +486,14 @@ qcrypto_tls_session_write(QCryptoTLSSession *session, if (ret == GNUTLS_E_AGAIN) { return QCRYPTO_TLS_SESSION_ERR_BLOCK; } else { - error_setg(errp, - "Cannot write to TLS channel: %s", - gnutls_strerror(ret)); + if (session->werr) { + error_propagate(errp, session->werr); + session->werr = NULL; + } else { + error_setg(errp, + "Cannot write to TLS channel: %s", + gnutls_strerror(ret)); + } return -1; } } @@ -477,9 +518,14 @@ qcrypto_tls_session_read(QCryptoTLSSession *session, gracefulTermination){ return 0; } else { - error_setg(errp, - "Cannot read from TLS channel: %s", - gnutls_strerror(ret)); + if (session->rerr) { + error_propagate(errp, session->rerr); + session->rerr = NULL; + } else { + error_setg(errp, + "Cannot read from TLS channel: %s", + gnutls_strerror(ret)); + } return -1; } } @@ -507,11 +553,21 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *session, ret == GNUTLS_E_AGAIN) { ret = 1; } else { - error_setg(errp, "TLS handshake failed: %s", - gnutls_strerror(ret)); + if (session->rerr || session->werr) { + error_setg(errp, "TLS handshake failed: %s: %s", + gnutls_strerror(ret), + error_get_pretty(session->rerr ? + session->rerr : session->werr)); + } else { + error_setg(errp, "TLS handshake failed: %s", + gnutls_strerror(ret)); + } ret = -1; } } + error_free(session->rerr); + error_free(session->werr); + session->rerr = session->werr = NULL; return ret; } |