From nobody Fri Oct 18 08:40:21 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1707487676; cv=none; d=zohomail.com; s=zohoarc; b=QrFUgcMkscyifv0HPD42TzqEnoZ2yTVpBtMHSWuAd2lH5+B8l9SbwaxlWj0AJ5NR+JRpjOlofz2kcZ7U0/5Ux0wjObaXMk6DazzdORboU7utFg+NytmKAccNI9xXFhOKs3IGlmp3QlBUkpSwMt5z4Tj7wwQS7Pm02f8++TfiUHc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1707487676; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=hJYcAf0Y5sYGj5/vIgJLjZ1whtgFXwC88K3zidLTLyE=; b=EYkpg97AzVyhConZNb34jLpbBn/92MWrTiQ1wG+tpIbg8HGnWdfvNz7ovTw/6uWyv/NBE17KvxTm0AkvTHEHKvTsWrgG6oLpxYgKs267UHOJUwTkZxO2zrYLYbP1/ukoLseWbbhrJoLvN6ZgLoPZgB4/TQZIJJKtVS9bfH+ppo4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1707487676781589.4173629370887; Fri, 9 Feb 2024 06:07:56 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rYRVo-00033D-03; Fri, 09 Feb 2024 09:05:52 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rYRVj-0002tk-Dg for qemu-devel@nongnu.org; Fri, 09 Feb 2024 09:05:48 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rYRVg-0004MF-C7 for qemu-devel@nongnu.org; Fri, 09 Feb 2024 09:05:47 -0500 Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-58-4YPm45gDMJ2NVWyUYAPlvg-1; Fri, 09 Feb 2024 09:05:40 -0500 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 0F10B1C0F478; Fri, 9 Feb 2024 14:05:40 +0000 (UTC) Received: from toolbox.redhat.com (unknown [10.42.28.105]) by smtp.corp.redhat.com (Postfix) with ESMTP id DA0C1111FF; Fri, 9 Feb 2024 14:05:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1707487543; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=hJYcAf0Y5sYGj5/vIgJLjZ1whtgFXwC88K3zidLTLyE=; b=OxA0OpKCjnl/86xklhK9mPxWDe/bbzncMEEu9FepFyWHGD5DiQkNwxeWv/xhRIiC2HedQM Ch+85dHizabCAF4ZQ6tO/BWPm841UYdZwpBCQFSTMgsKQ7krqW9ZXF7cKMi4V7+Zgz3ZH6 t20gUJ5HQ40auzRgi5hPR+ABTI6o+nA= X-MC-Unique: 4YPm45gDMJ2NVWyUYAPlvg-1 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: qemu-devel@nongnu.org Cc: Hanna Reitz , Gerd Hoffmann , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Markus Armbruster , Cleber Rosa , Paolo Bonzini , Eric Blake , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Eduardo Habkost , Stefan Weil , Kevin Wolf , Thomas Huth , Hyman Huang , Richard Henderson , John Snow , qemu-block@nongnu.org, =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Subject: [PULL 11/17] crypto: Support LUKS volume with detached header Date: Fri, 9 Feb 2024 14:04:59 +0000 Message-ID: <20240209140505.2536635-12-berrange@redhat.com> In-Reply-To: <20240209140505.2536635-1-berrange@redhat.com> References: <20240209140505.2536635-1-berrange@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.5 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=berrange@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -23 X-Spam_score: -2.4 X-Spam_bar: -- X-Spam_report: (-2.4 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.269, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1707487678817100007 From: Hyman Huang By enhancing the LUKS driver, it is possible to implement the LUKS volume with a detached header. 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 Take the raw-format image as an example to introduce the usage of the LUKS volume with a detached header: 1. prepare detached LUKS header images $ dd if=3D/dev/zero of=3Dtest-header.img bs=3D1M count=3D32 $ dd if=3D/dev/zero of=3Dtest-payload.img bs=3D1M count=3D1000 $ cryptsetup luksFormat --header test-header.img test-payload.img > --force-password --type luks1 2. block-add a protocol blockdev node of payload image $ virsh qemu-monitor-command vm '{"execute":"blockdev-add", > "arguments":{"node-name":"libvirt-1-storage", "driver":"file", > "filename":"test-payload.img"}}' 3. block-add a protocol blockdev node of LUKS header as above. $ virsh qemu-monitor-command vm '{"execute":"blockdev-add", > "arguments":{"node-name":"libvirt-2-storage", "driver":"file", > "filename": "test-header.img" }}' 4. 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-2-storage-secret0", "data":"abc123"}}' 5. block-add the raw-drived blockdev format node $ virsh qemu-monitor-command vm '{"execute":"blockdev-add", > "arguments":{"node-name":"libvirt-1-format", "driver":"raw", > "file":"libvirt-1-storage"}}' 6. block-add the luks-drived blockdev to link the raw disk with the LUKS header by specifying the field "header" $ virsh qemu-monitor-command vm '{"execute":"blockdev-add", > "arguments":{"node-name":"libvirt-2-format", "driver":"luks", > "file":"libvirt-1-format", "header":"libvirt-2-storage", > "key-secret":"libvirt-2-format-secret0"}}' 7. hot-plug the virtio-blk device finally $ virsh qemu-monitor-command vm '{"execute":"device_add", > "arguments": {"num-queues":"1", "driver":"virtio-blk-pci", > "drive": "libvirt-2-format", "id":"virtio-disk2"}}' Starting a VM with a LUKS volume with detached header is somewhat similar to hot-plug in that both maintaining the same json command while the starting VM changes the "blockdev-add/device_add" parameters to "blockdev/device". Signed-off-by: Hyman Huang Reviewed-by: Daniel P. Berrang=C3=A9 Signed-off-by: Daniel P. Berrang=C3=A9 --- block/crypto.c | 21 +++++++++++++++++++-- crypto/block-luks.c | 11 +++++++---- include/crypto/block.h | 5 +++++ qapi/block-core.json | 5 ++++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/block/crypto.c b/block/crypto.c index 921933a5e5..68656158e9 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -39,6 +39,7 @@ typedef struct BlockCrypto BlockCrypto; struct BlockCrypto { QCryptoBlock *block; bool updating_keys; + BdrvChild *header; /* Reference to the detached LUKS header */ }; =20 =20 @@ -63,12 +64,14 @@ static int block_crypto_read_func(QCryptoBlock *block, Error **errp) { BlockDriverState *bs =3D opaque; + BlockCrypto *crypto =3D bs->opaque; ssize_t ret; =20 GLOBAL_STATE_CODE(); GRAPH_RDLOCK_GUARD_MAINLOOP(); =20 - ret =3D bdrv_pread(bs->file, offset, buflen, buf, 0); + ret =3D bdrv_pread(crypto->header ? crypto->header : bs->file, + offset, buflen, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read encryption header"); return ret; @@ -84,12 +87,14 @@ static int block_crypto_write_func(QCryptoBlock *block, Error **errp) { BlockDriverState *bs =3D opaque; + BlockCrypto *crypto =3D bs->opaque; ssize_t ret; =20 GLOBAL_STATE_CODE(); GRAPH_RDLOCK_GUARD_MAINLOOP(); =20 - ret =3D bdrv_pwrite(bs->file, offset, buflen, buf, 0); + ret =3D bdrv_pwrite(crypto->header ? crypto->header : bs->file, + offset, buflen, buf, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write encryption header"); return ret; @@ -262,6 +267,8 @@ static int block_crypto_open_generic(QCryptoBlockFormat= format, int flags, Error **errp) { + ERRP_GUARD(); + BlockCrypto *crypto =3D bs->opaque; QemuOpts *opts =3D NULL; int ret; @@ -276,6 +283,13 @@ static int block_crypto_open_generic(QCryptoBlockForma= t format, return ret; } =20 + crypto->header =3D bdrv_open_child(NULL, options, "header", bs, + &child_of_bds, BDRV_CHILD_METADATA, + true, errp); + if (*errp !=3D NULL) { + return -EINVAL; + } + GRAPH_RDLOCK_GUARD_MAINLOOP(); =20 bs->supported_write_flags =3D BDRV_REQ_FUA & @@ -299,6 +313,9 @@ static int block_crypto_open_generic(QCryptoBlockFormat= format, if (flags & BDRV_O_NO_IO) { cflags |=3D QCRYPTO_BLOCK_OPEN_NO_IO; } + if (crypto->header !=3D NULL) { + cflags |=3D QCRYPTO_BLOCK_OPEN_DETACHED; + } crypto->block =3D qcrypto_block_open(open_opts, NULL, block_crypto_read_func, bs, diff --git a/crypto/block-luks.c b/crypto/block-luks.c index f0813d69b4..7e1235c213 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -468,12 +468,15 @@ qcrypto_block_luks_load_header(QCryptoBlock *block, * Does basic sanity checks on the LUKS header */ static int -qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp) +qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, + unsigned int flags, + Error **errp) { size_t i, j; =20 unsigned int header_sectors =3D QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET / QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; + bool detached =3D flags & QCRYPTO_BLOCK_OPEN_DETACHED; =20 if (memcmp(luks->header.magic, qcrypto_block_luks_magic, QCRYPTO_BLOCK_LUKS_MAGIC_LEN) !=3D 0) { @@ -505,7 +508,7 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS = *luks, Error **errp) return -1; } =20 - if (luks->header.payload_offset_sector < + if (!detached && luks->header.payload_offset_sector < DIV_ROUND_UP(QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) { error_setg(errp, "LUKS payload is overlapping with the header"); @@ -554,7 +557,7 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS = *luks, Error **errp) return -1; } =20 - if (start1 + len1 > luks->header.payload_offset_sector) { + if (!detached && start1 + len1 > luks->header.payload_offset_secto= r) { error_setg(errp, "Keyslot %zu is overlapping with the encrypted payl= oad", i); @@ -1214,7 +1217,7 @@ qcrypto_block_luks_open(QCryptoBlock *block, goto fail; } =20 - if (qcrypto_block_luks_check_header(luks, errp) < 0) { + if (qcrypto_block_luks_check_header(luks, flags, errp) < 0) { goto fail; } =20 diff --git a/include/crypto/block.h b/include/crypto/block.h index 4f63a37872..d0d97f5d12 100644 --- a/include/crypto/block.h +++ b/include/crypto/block.h @@ -66,6 +66,7 @@ bool qcrypto_block_has_format(QCryptoBlockFormat format, =20 typedef enum { QCRYPTO_BLOCK_OPEN_NO_IO =3D (1 << 0), + QCRYPTO_BLOCK_OPEN_DETACHED =3D (1 << 1), } QCryptoBlockOpenFlags; =20 /** @@ -95,6 +96,10 @@ typedef enum { * metadata such as the payload offset. There will be * no cipher or ivgen objects available. * + * If @flags contains QCRYPTO_BLOCK_OPEN_DETACHED then + * the open process will be optimized to skip the LUKS + * payload overlap check. + * * If any part of initializing the encryption context * fails an error will be returned. This could be due * to the volume being in the wrong format, a cipher diff --git a/qapi/block-core.json b/qapi/block-core.json index 781c9bd03e..e7d4fb539b 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3365,11 +3365,14 @@ # decryption key (since 2.6). Mandatory except when doing a # metadata-only probe of the image. # +# @header: block device holding a detached LUKS header. (since 9.0) +# # Since: 2.9 ## { 'struct': 'BlockdevOptionsLUKS', 'base': 'BlockdevOptionsGenericFormat', - 'data': { '*key-secret': 'str' } } + 'data': { '*key-secret': 'str', + '*header': 'BlockdevRef'} } =20 ## # @BlockdevOptionsGenericCOWFormat: --=20 2.43.0