aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2024-07-24 21:35:10 +1000
committerRichard Henderson <richard.henderson@linaro.org>2024-07-24 21:35:10 +1000
commit9726687f2fdfe7ae4a3014d78c2b2f639f75e303 (patch)
tree6b13139eab0ddd581d4ddeb4b5506db66b540dd7
parent5fcc25d5e3ce1d470f3db6bcb163185901f4cfe9 (diff)
parent97f7bf113eb50fcdaf0c73aa2ee01e5355abc073 (diff)
Merge tag 'misc-fixes-pull-request' of https://gitlab.com/berrange/qemu into staging
Crypto patches * Drop unused 'detached-header' QAPI field from LUKS create options * Improve tracing of TLS sockets and TLS chardevs * Improve error messages from TLS I/O failures * Add docs about use of LUKS detached header options * Allow building without libtasn1, but with GNUTLS * Fix detection of libgcrypt when libgcrypt-config is absent # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmagzXUACgkQvobrtBUQ # T9++chAAhCFgo5A/UjQGdl9UAOW/sdgOoHGE3E8Y6sSTQyv+EfHf1DO89JtAh4ft # d8Hz7Taul4k1wRm6Dxv2aCqH5iS1tgDE2ghGDNwn/zDtHNnjFx3+HcxBaAEcpt3O # FqvGeG6KdFO1t2UR2DMh1XbhfwygrHiIcSB2y8jrgi46ncS6JvLrFavjLTe7JBn9 # J3y/iYgQiVPN6UlIwUs1EquGdoTI/0SpHVirqHN/2yyrdRsGBsXZq5WI6Oli8zFL # VqJNmc5Dzo7ushoYG5Rpk83mmC26VuXO/JmXyJ/c7FeADLWUfc/SPPyAMxPGuwFr # DKg84ovRtq3yZIw8LPoUJOtbcu4Y7BSGwlolQjWegvsVTU6Bdk+teZVR9X64QbM2 # YBXzMkRHUKzR3rb0LewAKehP3n93aBypLln9ZMgg7wj92Rj8Dl/sylaBhDEkH/HQ # 2pMdSdAWqMnGHfnKPxyjflNO2PIsOenZUkDZwf9i7Ow6fU5n3fqvudVDTWjXpWPn # V7v9JGNPHocScJFRUqHSVqd2ZWaZX4F1TsvG6SGOmzDGR0IjBRlqos7OEdbAAH1x # IglizbTxD6M9ZWJrGt1sl6LSAwEp3oXgsWNdejq2+7I6H4BeUm4ACDbdrEjqG9aG # Ya/HpNT0PEzbGXm6qsuHY5z0agGtaPwdXLcSGnsv+a0rP/9nthY= # =ccYf # -----END PGP SIGNATURE----- # gpg: Signature made Wed 24 Jul 2024 07:46:29 PM AEST # gpg: using RSA key DAF3A6FDB26B62912D0E8E3FBE86EBB415104FDF # gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" [full] # gpg: aka "Daniel P. Berrange <berrange@redhat.com>" [full] * tag 'misc-fixes-pull-request' of https://gitlab.com/berrange/qemu: crypto: propagate errors from TLS session I/O callbacks crypto: push error reporting into TLS session I/O APIs crypto: drop gnutls debug logging support chardev: add tracing of socket error conditions meson: build chardev trace files when have_block qapi: drop unused QCryptoBlockCreateOptionsLUKS.detached-header meson.build: fix libgcrypt detection on system without libgcrypt-config docs/devel: Add introduction to LUKS volume with detached header crypto: Allow building with GnuTLS but without Libtasn1 crypto: Restrict pkix_asn1_tab[] to crypto-tls-x509-helpers.c crypto: Remove 'crypto-tls-x509-helpers.h' from crypto-tls-psk-helpers.c Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
-rw-r--r--MAINTAINERS3
-rw-r--r--chardev/char-socket.c37
-rw-r--r--chardev/trace-events10
-rw-r--r--crypto/init.c15
-rw-r--r--crypto/tlssession.c124
-rw-r--r--docs/devel/crypto.rst10
-rw-r--r--docs/devel/index-internals.rst1
-rw-r--r--docs/devel/luks-detached-header.rst182
-rw-r--r--include/crypto/tlssession.h33
-rw-r--r--io/channel-tls.c66
-rw-r--r--meson.build4
-rw-r--r--qapi/crypto.json5
-rw-r--r--tests/qtest/meson.build3
-rw-r--r--tests/unit/crypto-tls-psk-helpers.c1
-rw-r--r--tests/unit/crypto-tls-x509-helpers.c6
-rw-r--r--tests/unit/crypto-tls-x509-helpers.h3
-rw-r--r--tests/unit/meson.build6
-rw-r--r--tests/unit/pkix_asn1_tab.c.inc (renamed from tests/unit/pkix_asn1_tab.c)5
-rw-r--r--tests/unit/test-crypto-tlssession.c30
19 files changed, 418 insertions, 126 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index dd01288992..98eddf7ae1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3451,6 +3451,7 @@ Detached LUKS header
M: Hyman Huang <yong.huang@smartx.com>
S: Maintained
F: tests/qemu-iotests/tests/luks-detached-header
+F: docs/devel/luks-detached-header.rst
D-Bus
M: Marc-André Lureau <marcandre.lureau@redhat.com>
@@ -3484,7 +3485,7 @@ F: qapi/crypto.json
F: tests/unit/test-crypto-*
F: tests/bench/benchmark-crypto-*
F: tests/unit/crypto-tls-*
-F: tests/unit/pkix_asn1_tab.c
+F: tests/unit/pkix_asn1_tab.c.inc
F: qemu.sasl
Coroutines
diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index 812d7aa38a..1ca9441b1b 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -33,6 +33,7 @@
#include "qapi/clone-visitor.h"
#include "qapi/qapi-visit-sockets.h"
#include "qemu/yank.h"
+#include "trace.h"
#include "chardev/char-io.h"
#include "chardev/char-socket.h"
@@ -126,6 +127,7 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
if (ret < 0 && errno != EAGAIN) {
if (tcp_chr_read_poll(chr) <= 0) {
/* Perform disconnect and return error. */
+ trace_chr_socket_poll_err(chr, chr->label);
tcp_chr_disconnect_locked(chr);
} /* else let the read handler finish it properly */
}
@@ -279,15 +281,16 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
size_t i;
int *msgfds = NULL;
size_t msgfds_num = 0;
+ Error *err = NULL;
if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
ret = qio_channel_readv_full(s->ioc, &iov, 1,
&msgfds, &msgfds_num,
- 0, NULL);
+ 0, &err);
} else {
ret = qio_channel_readv_full(s->ioc, &iov, 1,
NULL, NULL,
- 0, NULL);
+ 0, &err);
}
if (msgfds_num) {
@@ -322,7 +325,11 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len)
errno = EAGAIN;
ret = -1;
} else if (ret == -1) {
+ trace_chr_socket_recv_err(chr, chr->label, error_get_pretty(err));
+ error_free(err);
errno = EIO;
+ } else if (ret == 0) {
+ trace_chr_socket_recv_eof(chr, chr->label);
}
return ret;
@@ -463,6 +470,7 @@ static void tcp_chr_disconnect_locked(Chardev *chr)
SocketChardev *s = SOCKET_CHARDEV(chr);
bool emit_close = s->state == TCP_CHARDEV_STATE_CONNECTED;
+ trace_chr_socket_disconnect(chr, chr->label);
tcp_chr_free_connection(chr);
if (s->listener) {
@@ -521,6 +529,7 @@ static gboolean tcp_chr_hup(QIOChannel *channel,
void *opaque)
{
Chardev *chr = CHARDEV(opaque);
+ trace_chr_socket_hangup(chr, chr->label);
tcp_chr_disconnect(chr);
return G_SOURCE_REMOVE;
}
@@ -672,15 +681,18 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
SocketChardev *s = user_data;
Chardev *chr = CHARDEV(s);
TCPChardevTelnetInit *init = s->telnet_init;
+ Error *err = NULL;
ssize_t ret;
assert(init);
- ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
+ ret = qio_channel_write(ioc, init->buf, init->buflen, &err);
if (ret < 0) {
if (ret == QIO_CHANNEL_ERR_BLOCK) {
ret = 0;
} else {
+ trace_chr_socket_write_err(chr, chr->label, error_get_pretty(err));
+ error_free(err);
tcp_chr_disconnect(chr);
goto end;
}
@@ -765,9 +777,9 @@ static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data)
Error *err = NULL;
if (qio_task_propagate_error(task, &err)) {
- error_reportf_err(err,
- "websock handshake of character device %s failed: ",
- chr->label);
+ trace_chr_socket_ws_handshake_err(chr, chr->label,
+ error_get_pretty(err));
+ error_free(err);
tcp_chr_disconnect(chr);
} else {
if (s->do_telnetopt) {
@@ -805,9 +817,9 @@ static void tcp_chr_tls_handshake(QIOTask *task,
Error *err = NULL;
if (qio_task_propagate_error(task, &err)) {
- error_reportf_err(err,
- "TLS handshake of character device %s failed: ",
- chr->label);
+ trace_chr_socket_tls_handshake_err(chr, chr->label,
+ error_get_pretty(err));
+ error_free(err);
tcp_chr_disconnect(chr);
} else {
if (s->is_websock) {
@@ -826,19 +838,22 @@ static void tcp_chr_tls_init(Chardev *chr)
SocketChardev *s = SOCKET_CHARDEV(chr);
QIOChannelTLS *tioc;
gchar *name;
+ Error *err = NULL;
if (s->is_listen) {
tioc = qio_channel_tls_new_server(
s->ioc, s->tls_creds,
s->tls_authz,
- NULL);
+ &err);
} else {
tioc = qio_channel_tls_new_client(
s->ioc, s->tls_creds,
s->addr->u.inet.host,
- NULL);
+ &err);
}
if (tioc == NULL) {
+ trace_chr_socket_tls_init_err(chr, chr->label, error_get_pretty(err));
+ error_free(err);
tcp_chr_disconnect(chr);
return;
}
diff --git a/chardev/trace-events b/chardev/trace-events
index 027107b0c1..7e97b8a988 100644
--- a/chardev/trace-events
+++ b/chardev/trace-events
@@ -17,3 +17,13 @@ spice_vmc_register_interface(void *scd) "spice vmc registered interface %p"
spice_vmc_unregister_interface(void *scd) "spice vmc unregistered interface %p"
spice_vmc_event(int event) "spice vmc event %d"
+# char-socket.c
+chr_socket_poll_err(void *chrdev, const char *label) "chardev socket poll error %p (%s)"
+chr_socket_recv_err(void *chrdev, const char *label, const char *err) "chardev socket recv error %p (%s): %s"
+chr_socket_recv_eof(void *chrdev, const char *label) "chardev socket recv end-of-file %p (%s)"
+chr_socket_write_err(void *chrdev, const char *label, const char *err) "chardev socket write error %p (%s): %s"
+chr_socket_disconnect(void *chrdev, const char *label) "chardev socket disconnect %p (%s)"
+chr_socket_hangup(void *chrdev, const char *label) "chardev socket hangup %p (%s)"
+chr_socket_ws_handshake_err(void *chrdev, const char *label, const char *err) "chardev socket websock handshake error %p (%s): %s"
+chr_socket_tls_handshake_err(void *chrdev, const char *label, const char *err) "chardev socket TLS handshake error %p (%s): %s"
+chr_socket_tls_init_err(void *chrdev, const char *label, const char *err) "chardev socket TLS init error %p (%s): %s"
diff --git a/crypto/init.c b/crypto/init.c
index fb7f1bff10..674d237fa9 100644
--- a/crypto/init.c
+++ b/crypto/init.c
@@ -34,14 +34,11 @@
#include "crypto/random.h"
-/* #define DEBUG_GNUTLS */
-#ifdef DEBUG_GNUTLS
-static void qcrypto_gnutls_log(int level, const char *str)
-{
- fprintf(stderr, "%d: %s", level, str);
-}
-#endif
+/*
+ * To debug GNUTLS see env vars listed in
+ * https://gnutls.org/manual/html_node/Debugging-and-auditing.html
+ */
int qcrypto_init(Error **errp)
{
#ifdef CONFIG_GNUTLS
@@ -53,10 +50,6 @@ int qcrypto_init(Error **errp)
gnutls_strerror(ret));
return -1;
}
-#ifdef DEBUG_GNUTLS
- gnutls_global_set_log_level(10);
- gnutls_global_set_log_function(qcrypto_gnutls_log);
-#endif
#endif
#ifdef CONFIG_GCRYPT
diff --git a/crypto/tlssession.c b/crypto/tlssession.c
index 1e98f44e0d..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"
@@ -441,23 +477,25 @@ qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session,
ssize_t
qcrypto_tls_session_write(QCryptoTLSSession *session,
const char *buf,
- size_t len)
+ size_t len,
+ Error **errp)
{
ssize_t ret = gnutls_record_send(session->handle, buf, len);
if (ret < 0) {
- switch (ret) {
- case GNUTLS_E_AGAIN:
- errno = EAGAIN;
- break;
- case GNUTLS_E_INTERRUPTED:
- errno = EINTR;
- break;
- default:
- errno = EIO;
- break;
+ if (ret == GNUTLS_E_AGAIN) {
+ return QCRYPTO_TLS_SESSION_ERR_BLOCK;
+ } else {
+ 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;
}
- ret = -1;
}
return ret;
@@ -467,26 +505,29 @@ qcrypto_tls_session_write(QCryptoTLSSession *session,
ssize_t
qcrypto_tls_session_read(QCryptoTLSSession *session,
char *buf,
- size_t len)
+ size_t len,
+ bool gracefulTermination,
+ Error **errp)
{
ssize_t ret = gnutls_record_recv(session->handle, buf, len);
if (ret < 0) {
- switch (ret) {
- case GNUTLS_E_AGAIN:
- errno = EAGAIN;
- break;
- case GNUTLS_E_INTERRUPTED:
- errno = EINTR;
- break;
- case GNUTLS_E_PREMATURE_TERMINATION:
- errno = ECONNABORTED;
- break;
- default:
- errno = EIO;
- break;
+ if (ret == GNUTLS_E_AGAIN) {
+ return QCRYPTO_TLS_SESSION_ERR_BLOCK;
+ } else if ((ret == GNUTLS_E_PREMATURE_TERMINATION) &&
+ gracefulTermination){
+ return 0;
+ } else {
+ 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;
}
- ret = -1;
}
return ret;
@@ -512,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;
}
@@ -605,9 +656,10 @@ qcrypto_tls_session_set_callbacks(
ssize_t
qcrypto_tls_session_write(QCryptoTLSSession *sess,
const char *buf,
- size_t len)
+ size_t len,
+ Error **errp)
{
- errno = -EIO;
+ error_setg(errp, "TLS requires GNUTLS support");
return -1;
}
@@ -615,9 +667,11 @@ qcrypto_tls_session_write(QCryptoTLSSession *sess,
ssize_t
qcrypto_tls_session_read(QCryptoTLSSession *sess,
char *buf,
- size_t len)
+ size_t len,
+ bool gracefulTermination,
+ Error **errp)
{
- errno = -EIO;
+ error_setg(errp, "TLS requires GNUTLS support");
return -1;
}
diff --git a/docs/devel/crypto.rst b/docs/devel/crypto.rst
new file mode 100644
index 0000000000..39b1c910e7
--- /dev/null
+++ b/docs/devel/crypto.rst
@@ -0,0 +1,10 @@
+.. _crypto-ref:
+
+====================
+Cryptography in QEMU
+====================
+
+.. toctree::
+ :maxdepth: 2
+
+ luks-detached-header
diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst
index 5636e9cf1d..4ac7725d72 100644
--- a/docs/devel/index-internals.rst
+++ b/docs/devel/index-internals.rst
@@ -20,3 +20,4 @@ Details about QEMU's various subsystems including how to add features to them.
vfio-iommufd
writing-monitor-commands
virtio-backends
+ crypto
diff --git a/docs/devel/luks-detached-header.rst b/docs/devel/luks-detached-header.rst
new file mode 100644
index 0000000000..94ec285c27
--- /dev/null
+++ b/docs/devel/luks-detached-header.rst
@@ -0,0 +1,182 @@
+================================
+LUKS volume with detached header
+================================
+
+Introduction
+============
+
+This document gives an overview of the design of LUKS volume with detached
+header and how to use it.
+
+Background
+==========
+
+The LUKS format has ability to store the header in a separate volume from
+the payload. We could extend the LUKS driver in QEMU to support this use
+case.
+
+Normally a LUKS volume has a layout:
+
+::
+
+ +-----------------------------------------------+
+ | | | |
+ disk | header | key material | disk payload data |
+ | | | |
+ +-----------------------------------------------+
+
+With a detached LUKS header, you need 2 disks so getting:
+
+::
+
+ +--------------------------+
+ disk1 | header | key material |
+ +--------------------------+
+ +---------------------+
+ disk2 | disk payload data |
+ +---------------------+
+
+There are a variety of benefits to doing this:
+
+ * Secrecy - the disk2 cannot be identified as containing LUKS
+ volume since there's no header
+ * Control - if access to the disk1 is restricted, then even
+ if someone has access to disk2 they can't unlock
+ it. Might be useful if you have disks on NFS but
+ want to restrict which host can launch a VM
+ instance from it, by dynamically providing access
+ to the header to a designated host
+ * Flexibility - your application data volume may be a given
+ size and it is inconvenient to resize it to
+ add encryption.You can store the LUKS header
+ separately and use the existing storage
+ volume for payload
+ * Recovery - corruption of a bit in the header may make the
+ entire payload inaccessible. It might be
+ convenient to take backups of the header. If
+ your primary disk header becomes corrupt, you
+ can unlock the data still by pointing to the
+ backup detached header
+
+Architecture
+============
+
+Take the qcow2 encryption, for example. The architecture of the
+LUKS volume with detached header is shown in the diagram below.
+
+There are two children of the root node: a file and a header.
+Data from the disk payload is stored in the file node. The
+LUKS header and key material are located in the header node,
+as previously mentioned.
+
+::
+
+ +-----------------------------+
+ Root node | foo[luks] |
+ +-----------------------------+
+ | |
+ file | header |
+ | |
+ +---------------------+ +------------------+
+ Child node |payload-format[qcow2]| |header-format[raw]|
+ +---------------------+ +------------------+
+ | |
+ file | file |
+ | |
+ +----------------------+ +---------------------+
+ Child node |payload-protocol[file]| |header-protocol[file]|
+ +----------------------+ +---------------------+
+ | |
+ | |
+ | |
+ Host storage Host storage
+
+Usage
+=====
+
+Create a LUKS disk with a detached header using qemu-img
+--------------------------------------------------------
+
+Shell commandline::
+
+ # qemu-img create --object secret,id=sec0,data=abc123 -f luks \
+ -o cipher-alg=aes-256,cipher-mode=xts -o key-secret=sec0 \
+ -o detached-header=true test-header.img
+ # qemu-img create -f qcow2 test-payload.qcow2 200G
+ # qemu-img info 'json:{"driver":"luks","file":{"filename": \
+ "test-payload.img"},"header":{"filename":"test-header.img"}}'
+
+Set up a VM's LUKS volume with a detached header
+------------------------------------------------
+
+Qemu commandline::
+
+ # qemu-system-x86_64 ... \
+ -object '{"qom-type":"secret","id":"libvirt-3-format-secret", \
+ "data":"abc123"}' \
+ -blockdev '{"driver":"file","filename":"/path/to/test-header.img", \
+ "node-name":"libvirt-1-storage"}' \
+ -blockdev '{"node-name":"libvirt-1-format","read-only":false, \
+ "driver":"raw","file":"libvirt-1-storage"}' \
+ -blockdev '{"driver":"file","filename":"/path/to/test-payload.qcow2", \
+ "node-name":"libvirt-2-storage"}' \
+ -blockdev '{"node-name":"libvirt-2-format","read-only":false, \
+ "driver":"qcow2","file":"libvirt-2-storage"}' \
+ -blockdev '{"node-name":"libvirt-3-format","driver":"luks", \
+ "file":"libvirt-2-format","header":"libvirt-1-format","key-secret": \
+ "libvirt-3-format-secret"}' \
+ -device '{"driver":"virtio-blk-pci","bus":XXX,"addr":YYY,"drive": \
+ "libvirt-3-format","id":"virtio-disk1"}'
+
+Add LUKS volume to a VM with a detached header
+----------------------------------------------
+
+1. object-add the secret for decrypting the cipher stored in
+ LUKS header above::
+
+ # virsh qemu-monitor-command vm '{"execute":"object-add", \
+ "arguments":{"qom-type":"secret", "id": \
+ "libvirt-4-format-secret", "data":"abc123"}}'
+
+2. block-add the protocol node for LUKS header::
+
+ # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \
+ "arguments":{"node-name":"libvirt-1-storage", "driver":"file", \
+ "filename": "/path/to/test-header.img" }}'
+
+3. block-add the raw-drived node for LUKS header::
+
+ # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \
+ "arguments":{"node-name":"libvirt-1-format", "driver":"raw", \
+ "file":"libvirt-1-storage"}}'
+
+4. block-add the protocol node for disk payload image::
+
+ # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \
+ "arguments":{"node-name":"libvirt-2-storage", "driver":"file", \
+ "filename":"/path/to/test-payload.qcow2"}}'
+
+5. block-add the qcow2-drived format node for disk payload data::
+
+ # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \
+ "arguments":{"node-name":"libvirt-2-format", "driver":"qcow2", \
+ "file":"libvirt-2-storage"}}'
+
+6. block-add the luks-drived format node to link the qcow2 disk
+ with the LUKS header by specifying the field "header"::
+
+ # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \
+ "arguments":{"node-name":"libvirt-3-format", "driver":"luks", \
+ "file":"libvirt-2-format", "header":"libvirt-1-format", \
+ "key-secret":"libvirt-2-format-secret"}}'
+
+7. hot-plug the virtio-blk device finally::
+
+ # virsh qemu-monitor-command vm '{"execute":"device_add", \
+ "arguments": {"driver":"virtio-blk-pci", \
+ "drive": "libvirt-3-format", "id":"virtio-disk2"}}
+
+TODO
+====
+
+1. Support the shared detached LUKS header within the VM.
diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h
index 571049bd0e..f694a5c3c5 100644
--- a/include/crypto/tlssession.h
+++ b/include/crypto/tlssession.h
@@ -107,6 +107,7 @@
typedef struct QCryptoTLSSession QCryptoTLSSession;
+#define QCRYPTO_TLS_SESSION_ERR_BLOCK -2
/**
* qcrypto_tls_session_new:
@@ -177,12 +178,18 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free)
int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess,
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,
size_t len,
- void *opaque);
+ void *opaque,
+ Error **errp);
typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf,
size_t len,
- void *opaque);
+ void *opaque,
+ Error **errp);
/**
* qcrypto_tls_session_set_callbacks:
@@ -212,6 +219,7 @@ void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess,
* @sess: the TLS session object
* @buf: the plain text to send
* @len: the length of @buf
+ * @errp: pointer to hold returned error object
*
* Encrypt @len bytes of the data in @buf and send
* it to the remote peer using the callback previously
@@ -221,32 +229,45 @@ void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess,
* qcrypto_tls_session_get_handshake_status() returns
* 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,
const char *buf,
- size_t len);
+ size_t len,
+ Error **errp);
/**
* qcrypto_tls_session_read:
* @sess: the TLS session object
* @buf: to fill with plain text received
* @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
* using the callback previously registered with
* qcrypto_tls_session_set_callbacks(), decrypt it and
* 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
* qcrypto_tls_session_get_handshake_status() returns
* 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,
char *buf,
- size_t len);
+ size_t len,
+ bool gracefulTermination,
+ Error **errp);
/**
* qcrypto_tls_session_check_pending:
diff --git a/io/channel-tls.c b/io/channel-tls.c
index 67b9700006..aab630e5ae 100644
--- a/io/channel-tls.c
+++ b/io/channel-tls.c
@@ -28,17 +28,16 @@
static ssize_t qio_channel_tls_write_handler(const char *buf,
size_t len,
- void *opaque)
+ void *opaque,
+ Error **errp)
{
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque);
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) {
- errno = EAGAIN;
- return -1;
+ return QCRYPTO_TLS_SESSION_ERR_BLOCK;
} else if (ret < 0) {
- errno = EIO;
return -1;
}
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,
size_t len,
- void *opaque)
+ void *opaque,
+ Error **errp)
{
QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque);
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) {
- errno = EAGAIN;
- return -1;
+ return QCRYPTO_TLS_SESSION_ERR_BLOCK;
} else if (ret < 0) {
- errno = EIO;
return -1;
}
return ret;
@@ -277,24 +275,19 @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc,
ssize_t got = 0;
for (i = 0 ; i < niov ; i++) {
- ssize_t ret = qcrypto_tls_session_read(tioc->session,
- iov[i].iov_base,
- iov[i].iov_len);
- if (ret < 0) {
- if (errno == EAGAIN) {
- if (got) {
- return got;
- } else {
- return QIO_CHANNEL_ERR_BLOCK;
- }
- } else if (errno == ECONNABORTED &&
- (qatomic_load_acquire(&tioc->shutdown) &
- QIO_CHANNEL_SHUTDOWN_READ)) {
- return 0;
+ ssize_t ret = qcrypto_tls_session_read(
+ tioc->session,
+ iov[i].iov_base,
+ iov[i].iov_len,
+ qatomic_load_acquire(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ,
+ errp);
+ if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) {
+ if (got) {
+ return got;
+ } else {
+ return QIO_CHANNEL_ERR_BLOCK;
}
-
- error_setg_errno(errp, errno,
- "Cannot read from TLS channel");
+ } else if (ret < 0) {
return -1;
}
got += ret;
@@ -321,18 +314,15 @@ static ssize_t qio_channel_tls_writev(QIOChannel *ioc,
for (i = 0 ; i < niov ; i++) {
ssize_t ret = qcrypto_tls_session_write(tioc->session,
iov[i].iov_base,
- iov[i].iov_len);
- if (ret <= 0) {
- if (errno == EAGAIN) {
- if (done) {
- return done;
- } else {
- return QIO_CHANNEL_ERR_BLOCK;
- }
+ iov[i].iov_len,
+ errp);
+ if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) {
+ if (done) {
+ return done;
+ } else {
+ return QIO_CHANNEL_ERR_BLOCK;
}
-
- error_setg_errno(errp, errno,
- "Cannot write to TLS channel");
+ } else if (ret < 0) {
return -1;
}
done += ret;
diff --git a/meson.build b/meson.build
index af9f0380e2..5613b62a4f 100644
--- a/meson.build
+++ b/meson.build
@@ -1696,7 +1696,6 @@ endif
if not gnutls_crypto.found()
if (not get_option('gcrypt').auto() or have_system) and not get_option('nettle').enabled()
gcrypt = dependency('libgcrypt', version: '>=1.8',
- method: 'config-tool',
required: get_option('gcrypt'))
# Debian has removed -lgpg-error from libgcrypt-config
# as it "spreads unnecessary dependencies" which in
@@ -1979,6 +1978,7 @@ endif
tasn1 = not_found
if gnutls.found()
tasn1 = dependency('libtasn1',
+ required: false,
method: 'pkg-config')
endif
keyutils = not_found
@@ -3343,6 +3343,7 @@ if have_block
trace_events_subdirs += [
'authz',
'block',
+ 'chardev',
'io',
'nbd',
'scsi',
@@ -3354,7 +3355,6 @@ if have_system
'audio',
'backends',
'backends/tpm',
- 'chardev',
'ebpf',
'hw/9pfs',
'hw/acpi',
diff --git a/qapi/crypto.json b/qapi/crypto.json
index e102be337b..f03bdab8c9 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -226,8 +226,6 @@
# @iter-time: number of milliseconds to spend in PBKDF passphrase
# processing. Currently defaults to 2000. (since 2.8)
#
-# @detached-header: create a detached LUKS header. (since 9.0)
-#
# Since: 2.6
##
{ 'struct': 'QCryptoBlockCreateOptionsLUKS',
@@ -237,8 +235,7 @@
'*ivgen-alg': 'QCryptoIVGenAlgorithm',
'*ivgen-hash-alg': 'QCryptoHashAlgorithm',
'*hash-alg': 'QCryptoHashAlgorithm',
- '*iter-time': 'int',
- '*detached-header': 'bool'}}
+ '*iter-time': 'int' }}
##
# @QCryptoBlockOpenOptions:
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index ff9200f882..e7ab2a4312 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -322,8 +322,7 @@ if gnutls.found()
migration_files += [files('../unit/crypto-tls-psk-helpers.c'), gnutls]
if tasn1.found()
- migration_files += [files('../unit/crypto-tls-x509-helpers.c',
- '../unit/pkix_asn1_tab.c'), tasn1]
+ migration_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1]
endif
endif
diff --git a/tests/unit/crypto-tls-psk-helpers.c b/tests/unit/crypto-tls-psk-helpers.c
index c6cc740772..36527fd655 100644
--- a/tests/unit/crypto-tls-psk-helpers.c
+++ b/tests/unit/crypto-tls-psk-helpers.c
@@ -20,7 +20,6 @@
#include "qemu/osdep.h"
-#include "crypto-tls-x509-helpers.h"
#include "crypto-tls-psk-helpers.h"
#include "qemu/sockets.h"
diff --git a/tests/unit/crypto-tls-x509-helpers.c b/tests/unit/crypto-tls-x509-helpers.c
index e9937f60d8..3e74ec5b5d 100644
--- a/tests/unit/crypto-tls-x509-helpers.c
+++ b/tests/unit/crypto-tls-x509-helpers.c
@@ -20,15 +20,19 @@
#include "qemu/osdep.h"
+#include <libtasn1.h>
+
#include "crypto-tls-x509-helpers.h"
#include "crypto/init.h"
#include "qemu/sockets.h"
+#include "pkix_asn1_tab.c.inc"
+
/*
* This stores some static data that is needed when
* encoding extensions in the x509 certs
*/
-asn1_node pkix_asn1;
+static asn1_node pkix_asn1;
/*
* To avoid consuming random entropy to generate keys,
diff --git a/tests/unit/crypto-tls-x509-helpers.h b/tests/unit/crypto-tls-x509-helpers.h
index 247e7160eb..562c160653 100644
--- a/tests/unit/crypto-tls-x509-helpers.h
+++ b/tests/unit/crypto-tls-x509-helpers.h
@@ -23,7 +23,6 @@
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
-#include <libtasn1.h>
#define QCRYPTO_TLS_TEST_CLIENT_NAME "ACME QEMU Client"
@@ -171,6 +170,4 @@ void test_tls_cleanup(const char *keyfile);
}; \
test_tls_generate_cert(&varname, cavarname.crt)
-extern const asn1_static_node pkix_asn1_tab[];
-
#endif
diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 26c109c968..490ab8182d 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -99,11 +99,11 @@ if have_block
tasn1.found() and \
host_os != 'windows'
tests += {
- 'test-crypto-tlscredsx509': ['crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c',
+ 'test-crypto-tlscredsx509': ['crypto-tls-x509-helpers.c',
tasn1, crypto, gnutls],
- 'test-crypto-tlssession': ['crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', 'crypto-tls-psk-helpers.c',
+ 'test-crypto-tlssession': ['crypto-tls-x509-helpers.c', 'crypto-tls-psk-helpers.c',
tasn1, crypto, gnutls],
- 'test-io-channel-tls': ['io-channel-helpers.c', 'crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c',
+ 'test-io-channel-tls': ['io-channel-helpers.c', 'crypto-tls-x509-helpers.c',
tasn1, io, crypto, gnutls]}
endif
if pam.found()
diff --git a/tests/unit/pkix_asn1_tab.c b/tests/unit/pkix_asn1_tab.c.inc
index 89521408a1..fe29c4102a 100644
--- a/tests/unit/pkix_asn1_tab.c
+++ b/tests/unit/pkix_asn1_tab.c.inc
@@ -3,10 +3,7 @@
* and is under copyright of various GNUTLS contributors.
*/
-#include "qemu/osdep.h"
-#include "crypto-tls-x509-helpers.h"
-
-const asn1_static_node pkix_asn1_tab[] = {
+static const asn1_static_node pkix_asn1_tab[] = {
{"PKIX1", 536875024, 0},
{0, 1073741836, 0},
{"id-ce", 1879048204, 0},
diff --git a/tests/unit/test-crypto-tlssession.c b/tests/unit/test-crypto-tlssession.c
index b12e7b6879..3395f73560 100644
--- a/tests/unit/test-crypto-tlssession.c
+++ b/tests/unit/test-crypto-tlssession.c
@@ -35,18 +35,40 @@
#define PSKFILE WORKDIR "keys.psk"
#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 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 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(