mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 08:13:54 -06:00
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>
This commit is contained in:
parent
57941c9c86
commit
97f7bf113e
4 changed files with 108 additions and 26 deletions
|
@ -44,6 +44,13 @@ struct QCryptoTLSSession {
|
||||||
QCryptoTLSSessionReadFunc readFunc;
|
QCryptoTLSSessionReadFunc readFunc;
|
||||||
void *opaque;
|
void *opaque;
|
||||||
char *peername;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error_free(session->rerr);
|
||||||
|
error_free(session->werr);
|
||||||
|
|
||||||
gnutls_deinit(session->handle);
|
gnutls_deinit(session->handle);
|
||||||
g_free(session->hostname);
|
g_free(session->hostname);
|
||||||
g_free(session->peername);
|
g_free(session->peername);
|
||||||
|
@ -67,13 +77,26 @@ static ssize_t
|
||||||
qcrypto_tls_session_push(void *opaque, const void *buf, size_t len)
|
qcrypto_tls_session_push(void *opaque, const void *buf, size_t len)
|
||||||
{
|
{
|
||||||
QCryptoTLSSession *session = opaque;
|
QCryptoTLSSession *session = opaque;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
if (!session->writeFunc) {
|
if (!session->writeFunc) {
|
||||||
errno = EIO;
|
errno = EIO;
|
||||||
return -1;
|
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)
|
qcrypto_tls_session_pull(void *opaque, void *buf, size_t len)
|
||||||
{
|
{
|
||||||
QCryptoTLSSession *session = opaque;
|
QCryptoTLSSession *session = opaque;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
if (!session->readFunc) {
|
if (!session->readFunc) {
|
||||||
errno = EIO;
|
errno = EIO;
|
||||||
return -1;
|
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"
|
#define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH"
|
||||||
|
@ -450,9 +486,14 @@ qcrypto_tls_session_write(QCryptoTLSSession *session,
|
||||||
if (ret == GNUTLS_E_AGAIN) {
|
if (ret == GNUTLS_E_AGAIN) {
|
||||||
return QCRYPTO_TLS_SESSION_ERR_BLOCK;
|
return QCRYPTO_TLS_SESSION_ERR_BLOCK;
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp,
|
if (session->werr) {
|
||||||
"Cannot write to TLS channel: %s",
|
error_propagate(errp, session->werr);
|
||||||
gnutls_strerror(ret));
|
session->werr = NULL;
|
||||||
|
} else {
|
||||||
|
error_setg(errp,
|
||||||
|
"Cannot write to TLS channel: %s",
|
||||||
|
gnutls_strerror(ret));
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -477,9 +518,14 @@ qcrypto_tls_session_read(QCryptoTLSSession *session,
|
||||||
gracefulTermination){
|
gracefulTermination){
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp,
|
if (session->rerr) {
|
||||||
"Cannot read from TLS channel: %s",
|
error_propagate(errp, session->rerr);
|
||||||
gnutls_strerror(ret));
|
session->rerr = NULL;
|
||||||
|
} else {
|
||||||
|
error_setg(errp,
|
||||||
|
"Cannot read from TLS channel: %s",
|
||||||
|
gnutls_strerror(ret));
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -507,11 +553,21 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *session,
|
||||||
ret == GNUTLS_E_AGAIN) {
|
ret == GNUTLS_E_AGAIN) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "TLS handshake failed: %s",
|
if (session->rerr || session->werr) {
|
||||||
gnutls_strerror(ret));
|
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;
|
ret = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
error_free(session->rerr);
|
||||||
|
error_free(session->werr);
|
||||||
|
session->rerr = session->werr = NULL;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,12 +178,18 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free)
|
||||||
int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess,
|
int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These must return QCRYPTO_TLS_SESSION_ERR_BLOCK if the I/O
|
||||||
|
* would block, but on other errors, must fill 'errp'
|
||||||
|
*/
|
||||||
typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const char *buf,
|
typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const char *buf,
|
||||||
size_t len,
|
size_t len,
|
||||||
void *opaque);
|
void *opaque,
|
||||||
|
Error **errp);
|
||||||
typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf,
|
typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf,
|
||||||
size_t len,
|
size_t len,
|
||||||
void *opaque);
|
void *opaque,
|
||||||
|
Error **errp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qcrypto_tls_session_set_callbacks:
|
* qcrypto_tls_session_set_callbacks:
|
||||||
|
|
|
@ -28,17 +28,16 @@
|
||||||
|
|
||||||
static ssize_t qio_channel_tls_write_handler(const char *buf,
|
static ssize_t qio_channel_tls_write_handler(const char *buf,
|
||||||
size_t len,
|
size_t len,
|
||||||
void *opaque)
|
void *opaque,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque);
|
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque);
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
ret = qio_channel_write(tioc->master, buf, len, NULL);
|
ret = qio_channel_write(tioc->master, buf, len, errp);
|
||||||
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
||||||
errno = EAGAIN;
|
return QCRYPTO_TLS_SESSION_ERR_BLOCK;
|
||||||
return -1;
|
|
||||||
} else if (ret < 0) {
|
} else if (ret < 0) {
|
||||||
errno = EIO;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -46,17 +45,16 @@ static ssize_t qio_channel_tls_write_handler(const char *buf,
|
||||||
|
|
||||||
static ssize_t qio_channel_tls_read_handler(char *buf,
|
static ssize_t qio_channel_tls_read_handler(char *buf,
|
||||||
size_t len,
|
size_t len,
|
||||||
void *opaque)
|
void *opaque,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque);
|
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque);
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
ret = qio_channel_read(tioc->master, buf, len, NULL);
|
ret = qio_channel_read(tioc->master, buf, len, errp);
|
||||||
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
||||||
errno = EAGAIN;
|
return QCRYPTO_TLS_SESSION_ERR_BLOCK;
|
||||||
return -1;
|
|
||||||
} else if (ret < 0) {
|
} else if (ret < 0) {
|
||||||
errno = EIO;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -35,18 +35,40 @@
|
||||||
#define PSKFILE WORKDIR "keys.psk"
|
#define PSKFILE WORKDIR "keys.psk"
|
||||||
#define KEYFILE WORKDIR "key-ctx.pem"
|
#define KEYFILE WORKDIR "key-ctx.pem"
|
||||||
|
|
||||||
static ssize_t testWrite(const char *buf, size_t len, void *opaque)
|
static ssize_t
|
||||||
|
testWrite(const char *buf, size_t len, void *opaque, Error **errp)
|
||||||
{
|
{
|
||||||
int *fd = opaque;
|
int *fd = opaque;
|
||||||
|
int ret;
|
||||||
|
|
||||||
return write(*fd, buf, len);
|
ret = write(*fd, buf, len);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EAGAIN) {
|
||||||
|
return QCRYPTO_TLS_SESSION_ERR_BLOCK;
|
||||||
|
} else {
|
||||||
|
error_setg_errno(errp, errno, "unable to write");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t testRead(char *buf, size_t len, void *opaque)
|
static ssize_t
|
||||||
|
testRead(char *buf, size_t len, void *opaque, Error **errp)
|
||||||
{
|
{
|
||||||
int *fd = opaque;
|
int *fd = opaque;
|
||||||
|
int ret;
|
||||||
|
|
||||||
return read(*fd, buf, len);
|
ret = read(*fd, buf, len);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EAGAIN) {
|
||||||
|
return QCRYPTO_TLS_SESSION_ERR_BLOCK;
|
||||||
|
} else {
|
||||||
|
error_setg_errno(errp, errno, "unable to read");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QCryptoTLSCreds *test_tls_creds_psk_create(
|
static QCryptoTLSCreds *test_tls_creds_psk_create(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue