crypto: push error reporting into TLS session I/O APIs

The current TLS session I/O APIs just return a synthetic errno
value on error, which has been translated from a gnutls error
value. This looses a large amount of valuable information that
distinguishes different scenarios.

Pushing population of the "Error *errp" object into the TLS
session I/O APIs gives more detailed error information.

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2024-03-15 14:07:58 +00:00
parent 305233349b
commit 57941c9c86
3 changed files with 68 additions and 63 deletions

View File

@ -441,23 +441,20 @@ qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session,
ssize_t ssize_t
qcrypto_tls_session_write(QCryptoTLSSession *session, qcrypto_tls_session_write(QCryptoTLSSession *session,
const char *buf, const char *buf,
size_t len) size_t len,
Error **errp)
{ {
ssize_t ret = gnutls_record_send(session->handle, buf, len); ssize_t ret = gnutls_record_send(session->handle, buf, len);
if (ret < 0) { if (ret < 0) {
switch (ret) { if (ret == GNUTLS_E_AGAIN) {
case GNUTLS_E_AGAIN: return QCRYPTO_TLS_SESSION_ERR_BLOCK;
errno = EAGAIN; } else {
break; error_setg(errp,
case GNUTLS_E_INTERRUPTED: "Cannot write to TLS channel: %s",
errno = EINTR; gnutls_strerror(ret));
break; return -1;
default:
errno = EIO;
break;
} }
ret = -1;
} }
return ret; return ret;
@ -467,26 +464,24 @@ qcrypto_tls_session_write(QCryptoTLSSession *session,
ssize_t ssize_t
qcrypto_tls_session_read(QCryptoTLSSession *session, qcrypto_tls_session_read(QCryptoTLSSession *session,
char *buf, char *buf,
size_t len) size_t len,
bool gracefulTermination,
Error **errp)
{ {
ssize_t ret = gnutls_record_recv(session->handle, buf, len); ssize_t ret = gnutls_record_recv(session->handle, buf, len);
if (ret < 0) { if (ret < 0) {
switch (ret) { if (ret == GNUTLS_E_AGAIN) {
case GNUTLS_E_AGAIN: return QCRYPTO_TLS_SESSION_ERR_BLOCK;
errno = EAGAIN; } else if ((ret == GNUTLS_E_PREMATURE_TERMINATION) &&
break; gracefulTermination){
case GNUTLS_E_INTERRUPTED: return 0;
errno = EINTR; } else {
break; error_setg(errp,
case GNUTLS_E_PREMATURE_TERMINATION: "Cannot read from TLS channel: %s",
errno = ECONNABORTED; gnutls_strerror(ret));
break; return -1;
default:
errno = EIO;
break;
} }
ret = -1;
} }
return ret; return ret;
@ -605,9 +600,10 @@ qcrypto_tls_session_set_callbacks(
ssize_t ssize_t
qcrypto_tls_session_write(QCryptoTLSSession *sess, qcrypto_tls_session_write(QCryptoTLSSession *sess,
const char *buf, const char *buf,
size_t len) size_t len,
Error **errp)
{ {
errno = -EIO; error_setg(errp, "TLS requires GNUTLS support");
return -1; return -1;
} }
@ -615,9 +611,11 @@ qcrypto_tls_session_write(QCryptoTLSSession *sess,
ssize_t ssize_t
qcrypto_tls_session_read(QCryptoTLSSession *sess, qcrypto_tls_session_read(QCryptoTLSSession *sess,
char *buf, char *buf,
size_t len) size_t len,
bool gracefulTermination,
Error **errp)
{ {
errno = -EIO; error_setg(errp, "TLS requires GNUTLS support");
return -1; return -1;
} }

View File

@ -107,6 +107,7 @@
typedef struct QCryptoTLSSession QCryptoTLSSession; typedef struct QCryptoTLSSession QCryptoTLSSession;
#define QCRYPTO_TLS_SESSION_ERR_BLOCK -2
/** /**
* qcrypto_tls_session_new: * qcrypto_tls_session_new:
@ -212,6 +213,7 @@ void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess,
* @sess: the TLS session object * @sess: the TLS session object
* @buf: the plain text to send * @buf: the plain text to send
* @len: the length of @buf * @len: the length of @buf
* @errp: pointer to hold returned error object
* *
* Encrypt @len bytes of the data in @buf and send * Encrypt @len bytes of the data in @buf and send
* it to the remote peer using the callback previously * it to the remote peer using the callback previously
@ -221,32 +223,45 @@ void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess,
* qcrypto_tls_session_get_handshake_status() returns * qcrypto_tls_session_get_handshake_status() returns
* QCRYPTO_TLS_HANDSHAKE_COMPLETE * QCRYPTO_TLS_HANDSHAKE_COMPLETE
* *
* Returns: the number of bytes sent, or -1 on error * Returns: the number of bytes sent,
* or QCRYPTO_TLS_SESSION_ERR_BLOCK if the write would block,
* or -1 on error.
*/ */
ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess,
const char *buf, const char *buf,
size_t len); size_t len,
Error **errp);
/** /**
* qcrypto_tls_session_read: * qcrypto_tls_session_read:
* @sess: the TLS session object * @sess: the TLS session object
* @buf: to fill with plain text received * @buf: to fill with plain text received
* @len: the length of @buf * @len: the length of @buf
* @gracefulTermination: treat premature termination as graceful EOF
* @errp: pointer to hold returned error object
* *
* Receive up to @len bytes of data from the remote peer * Receive up to @len bytes of data from the remote peer
* using the callback previously registered with * using the callback previously registered with
* qcrypto_tls_session_set_callbacks(), decrypt it and * qcrypto_tls_session_set_callbacks(), decrypt it and
* store it in @buf. * store it in @buf.
* *
* If @gracefulTermination is true, then a premature termination
* of the TLS session will be treated as indicating EOF, as
* opposed to an error.
*
* It is an error to call this before * It is an error to call this before
* qcrypto_tls_session_get_handshake_status() returns * qcrypto_tls_session_get_handshake_status() returns
* QCRYPTO_TLS_HANDSHAKE_COMPLETE * QCRYPTO_TLS_HANDSHAKE_COMPLETE
* *
* Returns: the number of bytes received, or -1 on error * Returns: the number of bytes received,
* or QCRYPTO_TLS_SESSION_ERR_BLOCK if the receive would block,
* or -1 on error.
*/ */
ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess,
char *buf, char *buf,
size_t len); size_t len,
bool gracefulTermination,
Error **errp);
/** /**
* qcrypto_tls_session_check_pending: * qcrypto_tls_session_check_pending:

View File

@ -277,24 +277,19 @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc,
ssize_t got = 0; ssize_t got = 0;
for (i = 0 ; i < niov ; i++) { for (i = 0 ; i < niov ; i++) {
ssize_t ret = qcrypto_tls_session_read(tioc->session, ssize_t ret = qcrypto_tls_session_read(
iov[i].iov_base, tioc->session,
iov[i].iov_len); iov[i].iov_base,
if (ret < 0) { iov[i].iov_len,
if (errno == EAGAIN) { qatomic_load_acquire(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ,
if (got) { errp);
return got; if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) {
} else { if (got) {
return QIO_CHANNEL_ERR_BLOCK; return got;
} } else {
} else if (errno == ECONNABORTED && return QIO_CHANNEL_ERR_BLOCK;
(qatomic_load_acquire(&tioc->shutdown) &
QIO_CHANNEL_SHUTDOWN_READ)) {
return 0;
} }
} else if (ret < 0) {
error_setg_errno(errp, errno,
"Cannot read from TLS channel");
return -1; return -1;
} }
got += ret; got += ret;
@ -321,18 +316,15 @@ static ssize_t qio_channel_tls_writev(QIOChannel *ioc,
for (i = 0 ; i < niov ; i++) { for (i = 0 ; i < niov ; i++) {
ssize_t ret = qcrypto_tls_session_write(tioc->session, ssize_t ret = qcrypto_tls_session_write(tioc->session,
iov[i].iov_base, iov[i].iov_base,
iov[i].iov_len); iov[i].iov_len,
if (ret <= 0) { errp);
if (errno == EAGAIN) { if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) {
if (done) { if (done) {
return done; return done;
} else { } else {
return QIO_CHANNEL_ERR_BLOCK; return QIO_CHANNEL_ERR_BLOCK;
}
} }
} else if (ret < 0) {
error_setg_errno(errp, errno,
"Cannot write to TLS channel");
return -1; return -1;
} }
done += ret; done += ret;