Add support for Clevis & Tang
resolves: rhbz#1809453 Fix CVE-2022-2211 Denial of Service in --key parameter resolves: rhbz#2101281
This commit is contained in:
		
							parent
							
								
									919dd2dd10
								
							
						
					
					
						commit
						dac85f9e02
					
				
							
								
								
									
										99
									
								
								0018-docs-guestfs-security-document-CVE-2022-2211.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								0018-docs-guestfs-security-document-CVE-2022-2211.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | ||||
| From fc0fd56abc2778a8473e9d421c73c9099dade4c4 Mon Sep 17 00:00:00 2001 | ||||
| From: Laszlo Ersek <lersek@redhat.com> | ||||
| Date: Tue, 28 Jun 2022 13:54:16 +0200 | ||||
| Subject: [PATCH] docs/guestfs-security: document CVE-2022-2211 | ||||
| 
 | ||||
| Short log for the common submodule, commit range | ||||
| f8de5508fe75..35467027f657: | ||||
| 
 | ||||
| Laszlo Ersek (2): | ||||
|       mlcustomize: factor out pkg install/update/uninstall from guestfs-tools | ||||
|       options: fix buffer overflow in get_keys() [CVE-2022-2211] | ||||
| 
 | ||||
| Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1809453 | ||||
| Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2100862 | ||||
| Signed-off-by: Laszlo Ersek <lersek@redhat.com> | ||||
| Message-Id: <20220628115418.5376-2-lersek@redhat.com> | ||||
| Reviewed-by: Richard W.M. Jones <rjones@redhat.com> | ||||
| (cherry picked from commit 99844660b48ed809e37378262c65d63df6ce4a53) | ||||
| ---
 | ||||
|  common                    |  2 +- | ||||
|  docs/guestfs-security.pod | 28 ++++++++++++++++++++++++++++ | ||||
|  2 files changed, 29 insertions(+), 1 deletion(-) | ||||
| 
 | ||||
| Submodule common f8de5508f..35467027f: | ||||
| diff --git a/common/options/keys.c b/common/options/keys.c
 | ||||
| index 798315c2e..d27a7123e 100644
 | ||||
| --- a/common/options/keys.c
 | ||||
| +++ b/common/options/keys.c
 | ||||
| @@ -128,17 +128,23 @@ read_first_line_from_file (const char *filename)
 | ||||
|  char ** | ||||
|  get_keys (struct key_store *ks, const char *device, const char *uuid) | ||||
|  { | ||||
| -  size_t i, j, len;
 | ||||
| +  size_t i, j, nmemb;
 | ||||
|    char **r; | ||||
|    char *s; | ||||
|   | ||||
|    /* We know the returned list must have at least one element and not | ||||
|     * more than ks->nr_keys. | ||||
|     */ | ||||
| -  len = 1;
 | ||||
| -  if (ks)
 | ||||
| -    len = MIN (1, ks->nr_keys);
 | ||||
| -  r = calloc (len+1, sizeof (char *));
 | ||||
| +  nmemb = 1;
 | ||||
| +  if (ks && ks->nr_keys > nmemb)
 | ||||
| +    nmemb = ks->nr_keys;
 | ||||
| +
 | ||||
| +  /* make room for the terminating NULL */
 | ||||
| +  if (nmemb == (size_t)-1)
 | ||||
| +    error (EXIT_FAILURE, 0, _("size_t overflow"));
 | ||||
| +  nmemb++;
 | ||||
| +
 | ||||
| +  r = calloc (nmemb, sizeof (char *));
 | ||||
|    if (r == NULL) | ||||
|      error (EXIT_FAILURE, errno, "calloc"); | ||||
|   | ||||
| diff --git a/docs/guestfs-security.pod b/docs/guestfs-security.pod
 | ||||
| index 9ceef5623..efa35b29d 100644
 | ||||
| --- a/docs/guestfs-security.pod
 | ||||
| +++ b/docs/guestfs-security.pod
 | ||||
| @@ -406,6 +406,34 @@ The libvirt backend is not affected.
 | ||||
|  The solution is to update qemu to a version containing the fix (see | ||||
|  L<https://lists.gnu.org/archive/html/qemu-devel/2018-06/msg01012.html>). | ||||
|   | ||||
| +=head2 CVE-2022-2211
 | ||||
| +
 | ||||
| +L<https://bugzilla.redhat.com/CVE-2022-2211>
 | ||||
| +
 | ||||
| +The C<get_keys> function in F<libguestfs-common/options/keys.c> collects
 | ||||
| +those I<--key> options from the command line into a new array that match
 | ||||
| +a particular block device that's being decrypted for inspection. The
 | ||||
| +function intends to size the result array such that potentially all
 | ||||
| +I<--key> options, plus a terminating C<NULL> element, fit into it. The
 | ||||
| +code mistakenly uses the C<MIN> macro instead of C<MAX>, and therefore
 | ||||
| +only one element is allocated before the C<NULL> terminator.
 | ||||
| +
 | ||||
| +Passing precisely two I<--key ID:...> options on the command line for
 | ||||
| +the encrypted block device C<ID> causes C<get_keys> to overwrite the
 | ||||
| +terminating C<NULL>, leading to an out-of-bounds read in
 | ||||
| +C<decrypt_mountables>, file F<libguestfs-common/options/decrypt.c>.
 | ||||
| +
 | ||||
| +Passing more than two I<--key ID:...> options on the command line for
 | ||||
| +the encrypted block device C<ID> causes C<get_keys> itself to perform
 | ||||
| +out-of-bounds writes. The most common symptom is a crash with C<SIGSEGV>
 | ||||
| +later on.
 | ||||
| +
 | ||||
| +This issue affects -- broadly speaking -- all libguestfs-based utilities
 | ||||
| +that accept I<--key>, namely: C<guestfish>, C<guestmount>, C<virt-cat>,
 | ||||
| +C<virt-customize>, C<virt-diff>, C<virt-edit>, C<virt-get-kernel>,
 | ||||
| +C<virt-inspector>, C<virt-log>, C<virt-ls>, C<virt-sparsify>,
 | ||||
| +C<virt-sysprep>, C<virt-tail>, C<virt-v2v>.
 | ||||
| +
 | ||||
|  =head1 SEE ALSO | ||||
|   | ||||
|  L<guestfs(3)>, | ||||
| -- 
 | ||||
| 2.31.1 | ||||
| 
 | ||||
							
								
								
									
										252
									
								
								0019-introduce-the-clevis_luks_unlock-API.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								0019-introduce-the-clevis_luks_unlock-API.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,252 @@ | ||||
| From 17eb49e3373e46f92768bdca0733e811603bcc80 Mon Sep 17 00:00:00 2001 | ||||
| From: Laszlo Ersek <lersek@redhat.com> | ||||
| Date: Thu, 30 Jun 2022 14:20:47 +0200 | ||||
| Subject: [PATCH] introduce the "clevis_luks_unlock" API | ||||
| 
 | ||||
| Introduce a new guestfs API called "clevis_luks_unlock". At the libguestfs | ||||
| level, it is quite simple; it wraps the "clevis luks unlock" guest command | ||||
| (implemented by the "clevis-luks-unlock" executable, which is in fact a | ||||
| shell script). | ||||
| 
 | ||||
| The complexity is instead in the network-based disk encryption | ||||
| (Clevis/Tang) scheme. Useful documentation: | ||||
| 
 | ||||
| - https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/security_hardening/index#configuring-automated-unlocking-of-encrypted-volumes-using-policy-based-decryption_security-hardening
 | ||||
| - https://github.com/latchset/clevis#clevis
 | ||||
| - https://github.com/latchset/tang#tang
 | ||||
| 
 | ||||
| The package providing "clevis-luks-unlock" is usually called | ||||
| "clevis-luks", occasionally "clevis". Some distros don't package clevis at | ||||
| all. Add the new API under a new option group (which may not be available) | ||||
| called "clevisluks". | ||||
| 
 | ||||
| Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1809453 | ||||
| Signed-off-by: Laszlo Ersek <lersek@redhat.com> | ||||
| Message-Id: <20220630122048.19335-3-lersek@redhat.com> | ||||
| Reviewed-by: Richard W.M. Jones <rjones@redhat.com> | ||||
| (cherry picked from commit 9a3e9a6c03eaffe60196bc4c7ae4699beae01dc3) | ||||
| ---
 | ||||
|  appliance/packagelist.in  |  4 +++ | ||||
|  daemon/Makefile.am        |  1 + | ||||
|  daemon/clevis-luks.c      | 58 +++++++++++++++++++++++++++++++++++++++ | ||||
|  generator/actions_core.ml | 40 +++++++++++++++++++++++++++ | ||||
|  generator/proc_nr.ml      |  1 + | ||||
|  lib/MAX_PROC_NR           |  2 +- | ||||
|  lib/guestfs.pod           | 19 ++++++++++--- | ||||
|  7 files changed, 120 insertions(+), 5 deletions(-) | ||||
|  create mode 100644 daemon/clevis-luks.c | ||||
| 
 | ||||
| diff --git a/appliance/packagelist.in b/appliance/packagelist.in
 | ||||
| index 77a07acc6..0b79edcdd 100644
 | ||||
| --- a/appliance/packagelist.in
 | ||||
| +++ b/appliance/packagelist.in
 | ||||
| @@ -23,6 +23,7 @@ dnl Basically the same with a few minor tweaks.
 | ||||
|  ifelse(UBUNTU,1,`define(`DEBIAN',1)') | ||||
|   | ||||
|  ifelse(REDHAT,1, | ||||
| +  clevis-luks
 | ||||
|    cryptsetup | ||||
|    cryptsetup-luks      dnl old name used before Fedora 17 | ||||
|    dhclient | ||||
| @@ -53,6 +54,7 @@ ifelse(DEBIAN,1,
 | ||||
|    bsdmainutils | ||||
|  dnl old name used in Jessie and earlier | ||||
|    btrfs-tools | ||||
| +  clevis-luks
 | ||||
|    cryptsetup | ||||
|    dash | ||||
|    extlinux | ||||
| @@ -92,6 +94,7 @@ dnl iproute has been renamed to iproute2
 | ||||
|  ifelse(ARCHLINUX,1, | ||||
|    cdrkit | ||||
|    cdrtools | ||||
| +  clevis
 | ||||
|    cryptsetup | ||||
|    dhclient | ||||
|    dhcpcd | ||||
| @@ -119,6 +122,7 @@ ifelse(SUSE,1,
 | ||||
|    augeas-lenses | ||||
|    btrfsprogs | ||||
|    cdrkit-cdrtools-compat | ||||
| +  clevis
 | ||||
|    cryptsetup | ||||
|    dhcpcd | ||||
|    dhcp-client | ||||
| diff --git a/daemon/Makefile.am b/daemon/Makefile.am
 | ||||
| index bbd49f9ea..f50faecd6 100644
 | ||||
| --- a/daemon/Makefile.am
 | ||||
| +++ b/daemon/Makefile.am
 | ||||
| @@ -98,6 +98,7 @@ guestfsd_SOURCES = \
 | ||||
|  	cap.c \ | ||||
|  	checksum.c \ | ||||
|  	cleanups.c \ | ||||
| +	clevis-luks.c \
 | ||||
|  	cmp.c \ | ||||
|  	command.c \ | ||||
|  	command.h \ | ||||
| diff --git a/daemon/clevis-luks.c b/daemon/clevis-luks.c
 | ||||
| new file mode 100644 | ||||
| index 000000000..d3d970d78
 | ||||
| --- /dev/null
 | ||||
| +++ b/daemon/clevis-luks.c
 | ||||
| @@ -0,0 +1,58 @@
 | ||||
| +/* libguestfs - the guestfsd daemon
 | ||||
| + * Copyright (C) 2009-2022 Red Hat Inc.
 | ||||
| + *
 | ||||
| + * This program is free software; you can redistribute it and/or modify
 | ||||
| + * it under the terms of the GNU General Public License as published by
 | ||||
| + * the Free Software Foundation; either version 2 of the License, or
 | ||||
| + * (at your option) any later version.
 | ||||
| + *
 | ||||
| + * This program is distributed in the hope that it will be useful,
 | ||||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||||
| + * GNU General Public License for more details.
 | ||||
| + *
 | ||||
| + * You should have received a copy of the GNU General Public License
 | ||||
| + * along with this program; if not, write to the Free Software
 | ||||
| + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | ||||
| + */
 | ||||
| +
 | ||||
| +#include <config.h>
 | ||||
| +
 | ||||
| +#include "daemon.h"
 | ||||
| +#include "actions.h"
 | ||||
| +#include "optgroups.h"
 | ||||
| +
 | ||||
| +#define MAX_ARGS 8
 | ||||
| +
 | ||||
| +int
 | ||||
| +optgroup_clevisluks_available (void)
 | ||||
| +{
 | ||||
| +  return prog_exists ("clevis-luks-unlock");
 | ||||
| +}
 | ||||
| +
 | ||||
| +int
 | ||||
| +do_clevis_luks_unlock (const char *device, const char *mapname)
 | ||||
| +{
 | ||||
| +  const char *argv[MAX_ARGS];
 | ||||
| +  size_t i = 0;
 | ||||
| +  int r;
 | ||||
| +  CLEANUP_FREE char *err = NULL;
 | ||||
| +
 | ||||
| +  ADD_ARG (argv, i, "clevis");
 | ||||
| +  ADD_ARG (argv, i, "luks");
 | ||||
| +  ADD_ARG (argv, i, "unlock");
 | ||||
| +  ADD_ARG (argv, i, "-d");
 | ||||
| +  ADD_ARG (argv, i, device);
 | ||||
| +  ADD_ARG (argv, i, "-n");
 | ||||
| +  ADD_ARG (argv, i, mapname);
 | ||||
| +  ADD_ARG (argv, i, NULL);
 | ||||
| +
 | ||||
| +  r = commandv (NULL, &err, argv);
 | ||||
| +  if (r == -1) {
 | ||||
| +    reply_with_error ("%s: %s: %s", device, mapname, err);
 | ||||
| +    return -1;
 | ||||
| +  }
 | ||||
| +
 | ||||
| +  udev_settle ();
 | ||||
| +  return 0;
 | ||||
| +}
 | ||||
| diff --git a/generator/actions_core.ml b/generator/actions_core.ml
 | ||||
| index 6cd42a290..3c9b0a9b2 100644
 | ||||
| --- a/generator/actions_core.ml
 | ||||
| +++ b/generator/actions_core.ml
 | ||||
| @@ -9676,4 +9676,44 @@ and I<not> the name of the underlying block device." };
 | ||||
|      shortdesc = "read directories entries"; | ||||
|      longdesc = "Internal function for readdir." }; | ||||
|   | ||||
| +  { defaults with
 | ||||
| +    name = "clevis_luks_unlock"; added = (1, 49, 3);
 | ||||
| +    style = RErr,
 | ||||
| +            [String (Device, "device"); String (PlainString, "mapname")],
 | ||||
| +            [];
 | ||||
| +    optional = Some "clevisluks";
 | ||||
| +    test_excuse = "needs networking and a configured Tang server";
 | ||||
| +    shortdesc = "open an encrypted LUKS block device with Clevis and Tang";
 | ||||
| +    longdesc = "\
 | ||||
| +This command opens a block device that has been encrypted according to
 | ||||
| +the Linux Unified Key Setup (LUKS) standard, using network-bound disk
 | ||||
| +encryption (NBDE).
 | ||||
| +
 | ||||
| +C<device> is the encrypted block device.
 | ||||
| +
 | ||||
| +The appliance will connect to the Tang servers noted in the tree of
 | ||||
| +Clevis pins that is bound to a keyslot of the LUKS header.  The Clevis
 | ||||
| +pin tree may comprise C<sss> (redudancy) pins as internal nodes
 | ||||
| +(optionally), and C<tang> pins as leaves.  C<tpm2> pins are not
 | ||||
| +supported.  The appliance unlocks the encrypted block device by
 | ||||
| +combining responses from the Tang servers with metadata from the LUKS
 | ||||
| +header; there is no C<key> parameter.
 | ||||
| +
 | ||||
| +This command will fail if networking has not been enabled for the
 | ||||
| +appliance. Refer to C<guestfs_set_network>.
 | ||||
| +
 | ||||
| +The command creates a new block device called F</dev/mapper/mapname>.
 | ||||
| +Reads and writes to this block device are decrypted from and encrypted
 | ||||
| +to the underlying C<device> respectively.  Close the decrypted block
 | ||||
| +device with C<guestfs_cryptsetup_close>.
 | ||||
| +
 | ||||
| +C<mapname> cannot be C<\"control\"> because that name is reserved by
 | ||||
| +device-mapper.
 | ||||
| +
 | ||||
| +If this block device contains LVM volume groups, then calling
 | ||||
| +C<guestfs_lvm_scan> with the C<activate> parameter C<true> will make
 | ||||
| +them visible.
 | ||||
| +
 | ||||
| +Use C<guestfs_list_dm_devices> to list all device mapper devices." };
 | ||||
| +
 | ||||
|  ] | ||||
| diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml
 | ||||
| index bdced51c9..edd9bd99d 100644
 | ||||
| --- a/generator/proc_nr.ml
 | ||||
| +++ b/generator/proc_nr.ml
 | ||||
| @@ -514,6 +514,7 @@ let proc_nr = [
 | ||||
|  509, "cryptsetup_close"; | ||||
|  510, "internal_list_rpm_applications"; | ||||
|  511, "internal_readdir"; | ||||
| +512, "clevis_luks_unlock"
 | ||||
|  ] | ||||
|   | ||||
|  (* End of list.  If adding a new entry, add it at the end of the list | ||||
| diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR
 | ||||
| index c0556fb20..4d0e90cbc 100644
 | ||||
| --- a/lib/MAX_PROC_NR
 | ||||
| +++ b/lib/MAX_PROC_NR
 | ||||
| @@ -1 +1 @@
 | ||||
| -511
 | ||||
| +512
 | ||||
| diff --git a/lib/guestfs.pod b/lib/guestfs.pod
 | ||||
| index 946ce2d36..0fbe114a5 100644
 | ||||
| --- a/lib/guestfs.pod
 | ||||
| +++ b/lib/guestfs.pod
 | ||||
| @@ -591,11 +591,22 @@ For Windows BitLocker it returns C<BitLocker>.
 | ||||
|  Then open these devices by calling L</guestfs_cryptsetup_open>. | ||||
|  Obviously you will require the passphrase! | ||||
|   | ||||
| +Passphrase-less unlocking is supported for LUKS (not BitLocker)
 | ||||
| +block devices that have been encrypted with network-bound disk
 | ||||
| +encryption (NBDE), using Clevis on the Linux guest side, and
 | ||||
| +Tang on a separate Linux server.  Open such devices with
 | ||||
| +L</guestfs_clevis_luks_unlock>.  The appliance will need
 | ||||
| +networking enabled (refer to L</guestfs_set_network>) and actual
 | ||||
| +connectivity to the Tang servers noted in the C<tang> Clevis
 | ||||
| +pins that are bound to the LUKS header.  (This includes the
 | ||||
| +ability to resolve the names of the Tang servers.)
 | ||||
| +
 | ||||
|  Opening an encrypted device creates a new device mapper device | ||||
| -called F</dev/mapper/mapname> (where C<mapname> is the
 | ||||
| -string you supply to L</guestfs_cryptsetup_open>).
 | ||||
| -Reads and writes to this mapper device are decrypted from and
 | ||||
| -encrypted to the underlying block device respectively.
 | ||||
| +called F</dev/mapper/mapname> (where C<mapname> is the string
 | ||||
| +you supply to L</guestfs_cryptsetup_open> or
 | ||||
| +L</guestfs_clevis_luks_unlock>). Reads and writes to this mapper
 | ||||
| +device are decrypted from and encrypted to the underlying block
 | ||||
| +device respectively.
 | ||||
|   | ||||
|  LVM volume groups on the device can be made visible by calling | ||||
|  L</guestfs_vgscan> followed by L</guestfs_vg_activate_all>. | ||||
| -- 
 | ||||
| 2.31.1 | ||||
| 
 | ||||
							
								
								
									
										414
									
								
								0020-guestfish-guestmount-enable-networking-for-key-ID-cl.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										414
									
								
								0020-guestfish-guestmount-enable-networking-for-key-ID-cl.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,414 @@ | ||||
| From 212708dee7c5c483dd0ce76889f7e20abba7f859 Mon Sep 17 00:00:00 2001 | ||||
| From: Laszlo Ersek <lersek@redhat.com> | ||||
| Date: Thu, 30 Jun 2022 14:20:48 +0200 | ||||
| Subject: [PATCH] guestfish, guestmount: enable networking for "--key | ||||
|  ID:clevis" | ||||
| 
 | ||||
| Call the C-language helper key_store_requires_network() in guestfish and | ||||
| guestmount. | ||||
| 
 | ||||
| (Short log for the "common" submodule, commit range | ||||
| 35467027f657..af6cb55bc58a: | ||||
| 
 | ||||
| Laszlo Ersek (12): | ||||
|       options: fix UUID comparison logic bug in get_keys() | ||||
|       mltools/tools_utils: remove unused function "key_store_to_cli" | ||||
|       mltools/tools_utils: allow multiple "--key" options for OCaml tools too | ||||
|       options: replace NULL-termination with number-of-elements in get_keys() | ||||
|       options: wrap each passphrase from get_keys() into a struct | ||||
|       options: add back-end for LUKS decryption with Clevis+Tang | ||||
|       options: introduce selector type "key_clevis" | ||||
|       options: generalize "--key" selector parsing for C-language utilities | ||||
|       mltools/tools_utils-c: handle internal type error with abort() | ||||
|       mltools/tools_utils: generalize "--key" selector parsing for OCaml utils | ||||
|       options, mltools/tools_utils: parse "--key ID:clevis" options | ||||
|       options, mltools/tools_utils: add helper for network dependency | ||||
| ). | ||||
| 
 | ||||
| Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1809453 | ||||
| Signed-off-by: Laszlo Ersek <lersek@redhat.com> | ||||
| Reviewed-by: Richard W.M. Jones <rjones@redhat.com> | ||||
| Message-Id: <20220630122048.19335-4-lersek@redhat.com> | ||||
| (cherry picked from commit 6a5b44f538065a9f661510234a4235bf38348213) | ||||
| ---
 | ||||
|  common            | 2 +- | ||||
|  fish/fish.c       | 3 +++ | ||||
|  fuse/guestmount.c | 4 ++++ | ||||
|  3 files changed, 8 insertions(+), 1 deletion(-) | ||||
| 
 | ||||
| Submodule common 35467027f..af6cb55bc: | ||||
| diff --git a/common/options/decrypt.c b/common/options/decrypt.c
 | ||||
| index 1cd7b627e..97c8b88d1 100644
 | ||||
| --- a/common/options/decrypt.c
 | ||||
| +++ b/common/options/decrypt.c
 | ||||
| @@ -124,10 +124,10 @@ decrypt_mountables (guestfs_h *g, const char * const *mountables,
 | ||||
|    while ((mountable = *mnt_scan++) != NULL) { | ||||
|      CLEANUP_FREE char *type = NULL; | ||||
|      CLEANUP_FREE char *uuid = NULL; | ||||
| -    CLEANUP_FREE_STRING_LIST char **keys = NULL;
 | ||||
| +    struct matching_key *keys;
 | ||||
| +    size_t nr_matches;
 | ||||
|      CLEANUP_FREE char *mapname = NULL; | ||||
| -    const char * const *key_scan;
 | ||||
| -    const char *key;
 | ||||
| +    size_t scan;
 | ||||
|   | ||||
|      type = guestfs_vfs_type (g, mountable); | ||||
|      if (type == NULL) | ||||
| @@ -144,33 +144,45 @@ decrypt_mountables (guestfs_h *g, const char * const *mountables,
 | ||||
|      /* Grab the keys that we should try with this device, based on device name, | ||||
|       * or UUID (if any). | ||||
|       */ | ||||
| -    keys = get_keys (ks, mountable, uuid);
 | ||||
| -    assert (keys[0] != NULL);
 | ||||
| +    keys = get_keys (ks, mountable, uuid, &nr_matches);
 | ||||
| +    assert (nr_matches > 0);
 | ||||
|   | ||||
|      /* Generate a node name for the plaintext (decrypted) device node. */ | ||||
|      if (uuid == NULL || asprintf (&mapname, "luks-%s", uuid) == -1) | ||||
|        mapname = make_mapname (mountable); | ||||
|   | ||||
|      /* Try each key in turn. */ | ||||
| -    key_scan = (const char * const *)keys;
 | ||||
| -    while ((key = *key_scan++) != NULL) {
 | ||||
| +    for (scan = 0; scan < nr_matches; ++scan) {
 | ||||
| +      struct matching_key *key = keys + scan;
 | ||||
|        int r; | ||||
|   | ||||
|        guestfs_push_error_handler (g, NULL, NULL); | ||||
| -      r = guestfs_cryptsetup_open (g, mountable, key, mapname, -1);
 | ||||
| +      assert (key->clevis == (key->passphrase == NULL));
 | ||||
| +      if (key->clevis)
 | ||||
| +#ifdef GUESTFS_HAVE_CLEVIS_LUKS_UNLOCK
 | ||||
| +        r = guestfs_clevis_luks_unlock (g, mountable, mapname);
 | ||||
| +#else
 | ||||
| +        error (EXIT_FAILURE, 0,
 | ||||
| +               _("'clevis_luks_unlock', needed for decrypting %s, is "
 | ||||
| +                 "unavailable in this libguestfs version"), mountable);
 | ||||
| +#endif
 | ||||
| +      else
 | ||||
| +        r = guestfs_cryptsetup_open (g, mountable, key->passphrase, mapname,
 | ||||
| +                                     -1);
 | ||||
|        guestfs_pop_error_handler (g); | ||||
|   | ||||
|        if (r == 0) | ||||
|          break; | ||||
|      } | ||||
|   | ||||
| -    if (key == NULL)
 | ||||
| +    if (scan == nr_matches)
 | ||||
|        error (EXIT_FAILURE, 0, | ||||
|               _("could not find key to open LUKS encrypted %s.\n\n" | ||||
|                 "Try using --key on the command line.\n\n" | ||||
|                 "Original error: %s (%d)"), | ||||
|               mountable, guestfs_last_error (g), guestfs_last_errno (g)); | ||||
|   | ||||
| +    free_keys (keys, nr_matches);
 | ||||
|      decrypted_some = true; | ||||
|    } | ||||
|   | ||||
| diff --git a/common/options/key-option.pod b/common/options/key-option.pod
 | ||||
| index 90a3b15c5..6bc04df17 100644
 | ||||
| --- a/common/options/key-option.pod
 | ||||
| +++ b/common/options/key-option.pod
 | ||||
| @@ -14,4 +14,13 @@ Use the specified C<KEY_STRING> as passphrase.
 | ||||
|   | ||||
|  Read the passphrase from F<FILENAME>. | ||||
|   | ||||
| +=item B<--key> C<ID>:clevis
 | ||||
| +
 | ||||
| +Attempt passphrase-less unlocking for C<ID> with Clevis, over the
 | ||||
| +network.  Please refer to L<guestfs(3)/ENCRYPTED DISKS> for more
 | ||||
| +information on network-bound disk encryption (NBDE).
 | ||||
| +
 | ||||
| +Note that if any such option is present on the command line, QEMU user
 | ||||
| +networking will be automatically enabled for the libguestfs appliance.
 | ||||
| +
 | ||||
|  =back | ||||
| diff --git a/common/options/keys.c b/common/options/keys.c
 | ||||
| index d27a7123e..d987ae561 100644
 | ||||
| --- a/common/options/keys.c
 | ||||
| +++ b/common/options/keys.c
 | ||||
| @@ -125,11 +125,12 @@ read_first_line_from_file (const char *filename)
 | ||||
|   * keystore.  There may be multiple.  If none are read from the | ||||
|   * keystore, ask the user. | ||||
|   */ | ||||
| -char **
 | ||||
| -get_keys (struct key_store *ks, const char *device, const char *uuid)
 | ||||
| +struct matching_key *
 | ||||
| +get_keys (struct key_store *ks, const char *device, const char *uuid,
 | ||||
| +          size_t *nr_matches)
 | ||||
|  { | ||||
| -  size_t i, j, nmemb;
 | ||||
| -  char **r;
 | ||||
| +  size_t i, nmemb;
 | ||||
| +  struct matching_key *r, *match;
 | ||||
|    char *s; | ||||
|   | ||||
|    /* We know the returned list must have at least one element and not | ||||
| @@ -139,22 +140,20 @@ get_keys (struct key_store *ks, const char *device, const char *uuid)
 | ||||
|    if (ks && ks->nr_keys > nmemb) | ||||
|      nmemb = ks->nr_keys; | ||||
|   | ||||
| -  /* make room for the terminating NULL */
 | ||||
| -  if (nmemb == (size_t)-1)
 | ||||
| +  if (nmemb > (size_t)-1 / sizeof *r)
 | ||||
|      error (EXIT_FAILURE, 0, _("size_t overflow")); | ||||
| -  nmemb++;
 | ||||
|   | ||||
| -  r = calloc (nmemb, sizeof (char *));
 | ||||
| +  r = malloc (nmemb * sizeof *r);
 | ||||
|    if (r == NULL) | ||||
| -    error (EXIT_FAILURE, errno, "calloc");
 | ||||
| +    error (EXIT_FAILURE, errno, "malloc");
 | ||||
|   | ||||
| -  j = 0;
 | ||||
| +  match = r;
 | ||||
|   | ||||
|    if (ks) { | ||||
|      for (i = 0; i < ks->nr_keys; ++i) { | ||||
|        struct key_store_key *key = &ks->keys[i]; | ||||
|   | ||||
| -      if (STRNEQ (key->id, device) && (uuid && STRNEQ (key->id, uuid)))
 | ||||
| +      if (STRNEQ (key->id, device) && (!uuid || STRNEQ (key->id, uuid)))
 | ||||
|          continue; | ||||
|   | ||||
|        switch (key->type) { | ||||
| @@ -162,68 +161,101 @@ get_keys (struct key_store *ks, const char *device, const char *uuid)
 | ||||
|          s = strdup (key->string.s); | ||||
|          if (!s) | ||||
|            error (EXIT_FAILURE, errno, "strdup"); | ||||
| -        r[j++] = s;
 | ||||
| +        match->clevis = false;
 | ||||
| +        match->passphrase = s;
 | ||||
| +        ++match;
 | ||||
|          break; | ||||
|        case key_file: | ||||
|          s = read_first_line_from_file (key->file.name); | ||||
| -        r[j++] = s;
 | ||||
| +        match->clevis = false;
 | ||||
| +        match->passphrase = s;
 | ||||
| +        ++match;
 | ||||
| +        break;
 | ||||
| +      case key_clevis:
 | ||||
| +        match->clevis = true;
 | ||||
| +        match->passphrase = NULL;
 | ||||
| +        ++match;
 | ||||
|          break; | ||||
|        } | ||||
|      } | ||||
|    } | ||||
|   | ||||
| -  if (j == 0) {
 | ||||
| +  if (match == r) {
 | ||||
|      /* Key not found in the key store, ask the user for it. */ | ||||
|      s = read_key (device); | ||||
|      if (!s) | ||||
|        error (EXIT_FAILURE, 0, _("could not read key from user")); | ||||
| -    r[0] = s;
 | ||||
| +    match->clevis = false;
 | ||||
| +    match->passphrase = s;
 | ||||
| +    ++match;
 | ||||
|    } | ||||
|   | ||||
| +  *nr_matches = (size_t)(match - r);
 | ||||
|    return r; | ||||
|  } | ||||
|   | ||||
| +void
 | ||||
| +free_keys (struct matching_key *keys, size_t nr_matches)
 | ||||
| +{
 | ||||
| +  size_t i;
 | ||||
| +
 | ||||
| +  for (i = 0; i < nr_matches; ++i) {
 | ||||
| +    struct matching_key *key = keys + i;
 | ||||
| +
 | ||||
| +    assert (key->clevis == (key->passphrase == NULL));
 | ||||
| +    if (!key->clevis)
 | ||||
| +      free (key->passphrase);
 | ||||
| +  }
 | ||||
| +  free (keys);
 | ||||
| +}
 | ||||
| +
 | ||||
|  struct key_store * | ||||
|  key_store_add_from_selector (struct key_store *ks, const char *selector) | ||||
|  { | ||||
| -  CLEANUP_FREE_STRING_LIST char **fields =
 | ||||
| -    guestfs_int_split_string (':', selector);
 | ||||
| +  CLEANUP_FREE_STRING_LIST char **fields = NULL;
 | ||||
| +  size_t field_count;
 | ||||
|    struct key_store_key key; | ||||
|   | ||||
| +  fields = guestfs_int_split_string (':', selector);
 | ||||
|    if (!fields) | ||||
|      error (EXIT_FAILURE, errno, "guestfs_int_split_string"); | ||||
| +  field_count = guestfs_int_count_strings (fields);
 | ||||
|   | ||||
| -  if (guestfs_int_count_strings (fields) != 3) {
 | ||||
| -   invalid_selector:
 | ||||
| -    error (EXIT_FAILURE, 0, "invalid selector for --key: %s", selector);
 | ||||
| -  }
 | ||||
| -
 | ||||
| -  /* 1: device */
 | ||||
| +  /* field#0: ID */
 | ||||
| +  if (field_count < 1)
 | ||||
| +    error (EXIT_FAILURE, 0, _("selector '%s': missing ID"), selector);
 | ||||
|    key.id = strdup (fields[0]); | ||||
|    if (!key.id) | ||||
|      error (EXIT_FAILURE, errno, "strdup"); | ||||
|   | ||||
| -  /* 2: key type */
 | ||||
| -  if (STREQ (fields[1], "key"))
 | ||||
| +  /* field#1...: TYPE, and TYPE-specific properties */
 | ||||
| +  if (field_count < 2)
 | ||||
| +    error (EXIT_FAILURE, 0, _("selector '%s': missing TYPE"), selector);
 | ||||
| +
 | ||||
| +  if (STREQ (fields[1], "key")) {
 | ||||
|      key.type = key_string; | ||||
| -  else if (STREQ (fields[1], "file"))
 | ||||
| -    key.type = key_file;
 | ||||
| -  else
 | ||||
| -    goto invalid_selector;
 | ||||
| -
 | ||||
| -  /* 3: actual key */
 | ||||
| -  switch (key.type) {
 | ||||
| -  case key_string:
 | ||||
| +    if (field_count != 3)
 | ||||
| +      error (EXIT_FAILURE, 0,
 | ||||
| +             _("selector '%s': missing KEY_STRING, or too many fields"),
 | ||||
| +             selector);
 | ||||
|      key.string.s = strdup (fields[2]); | ||||
|      if (!key.string.s) | ||||
|        error (EXIT_FAILURE, errno, "strdup"); | ||||
| -    break;
 | ||||
| -  case key_file:
 | ||||
| +  } else if (STREQ (fields[1], "file")) {
 | ||||
| +    key.type = key_file;
 | ||||
| +    if (field_count != 3)
 | ||||
| +      error (EXIT_FAILURE, 0,
 | ||||
| +             _("selector '%s': missing FILENAME, or too many fields"),
 | ||||
| +             selector);
 | ||||
|      key.file.name = strdup (fields[2]); | ||||
|      if (!key.file.name) | ||||
|        error (EXIT_FAILURE, errno, "strdup"); | ||||
| -    break;
 | ||||
| -  }
 | ||||
| +  } else if (STREQ (fields[1], "clevis")) {
 | ||||
| +    key.type = key_clevis;
 | ||||
| +    if (field_count != 2)
 | ||||
| +      error (EXIT_FAILURE, 0, _("selector '%s': too many fields"), selector);
 | ||||
| +  } else
 | ||||
| +    error (EXIT_FAILURE, 0, _("selector '%s': invalid TYPE"), selector);
 | ||||
|   | ||||
|    return key_store_import_key (ks, &key); | ||||
|  } | ||||
| @@ -252,6 +284,21 @@ key_store_import_key (struct key_store *ks, const struct key_store_key *key)
 | ||||
|    return ks; | ||||
|  } | ||||
|   | ||||
| +bool
 | ||||
| +key_store_requires_network (const struct key_store *ks)
 | ||||
| +{
 | ||||
| +  size_t i;
 | ||||
| +
 | ||||
| +  if (ks == NULL)
 | ||||
| +    return false;
 | ||||
| +
 | ||||
| +  for (i = 0; i < ks->nr_keys; ++i)
 | ||||
| +    if (ks->keys[i].type == key_clevis)
 | ||||
| +      return true;
 | ||||
| +
 | ||||
| +  return false;
 | ||||
| +}
 | ||||
| +
 | ||||
|  void | ||||
|  free_key_store (struct key_store *ks) | ||||
|  { | ||||
| @@ -270,6 +317,9 @@ free_key_store (struct key_store *ks)
 | ||||
|      case key_file: | ||||
|        free (key->file.name); | ||||
|        break; | ||||
| +    case key_clevis:
 | ||||
| +      /* nothing */
 | ||||
| +      break;
 | ||||
|      } | ||||
|      free (key->id); | ||||
|    } | ||||
| diff --git a/common/options/options.h b/common/options/options.h
 | ||||
| index 80df91a85..60d5d8064 100644
 | ||||
| --- a/common/options/options.h
 | ||||
| +++ b/common/options/options.h
 | ||||
| @@ -115,6 +115,7 @@ struct key_store_key {
 | ||||
|    enum { | ||||
|      key_string,             /* key specified as string */ | ||||
|      key_file,               /* key stored in a file */ | ||||
| +    key_clevis,             /* key reconstructed with Clevis+Tang */
 | ||||
|    } type; | ||||
|    union { | ||||
|      struct { | ||||
| @@ -134,6 +135,19 @@ struct key_store {
 | ||||
|    size_t nr_keys; | ||||
|  }; | ||||
|   | ||||
| +/* A key matching a particular ID (pathname of the libguestfs device node that
 | ||||
| + * stands for the encrypted block device, or LUKS UUID).
 | ||||
| + */
 | ||||
| +struct matching_key {
 | ||||
| +  /* True iff the passphrase should be reconstructed using Clevis, talking to
 | ||||
| +   * Tang servers over the network.
 | ||||
| +   */
 | ||||
| +  bool clevis;
 | ||||
| +
 | ||||
| +  /* Explicit passphrase, otherwise. */
 | ||||
| +  char *passphrase;
 | ||||
| +};
 | ||||
| +
 | ||||
|  /* in config.c */ | ||||
|  extern void parse_config (void); | ||||
|   | ||||
| @@ -151,9 +165,12 @@ extern void print_inspect_prompt (void);
 | ||||
|   | ||||
|  /* in key.c */ | ||||
|  extern char *read_key (const char *param); | ||||
| -extern char **get_keys (struct key_store *ks, const char *device, const char *uuid);
 | ||||
| +extern struct matching_key *get_keys (struct key_store *ks, const char *device,
 | ||||
| +                                      const char *uuid, size_t *nr_matches);
 | ||||
| +extern void free_keys (struct matching_key *keys, size_t nr_matches);
 | ||||
|  extern struct key_store *key_store_add_from_selector (struct key_store *ks, const char *selector); | ||||
|  extern struct key_store *key_store_import_key (struct key_store *ks, const struct key_store_key *key); | ||||
| +extern bool key_store_requires_network (const struct key_store *ks);
 | ||||
|  extern void free_key_store (struct key_store *ks); | ||||
|   | ||||
|  /* in options.c */ | ||||
| diff --git a/fish/fish.c b/fish/fish.c
 | ||||
| index 23d9bb94f..19e3d2799 100644
 | ||||
| --- a/fish/fish.c
 | ||||
| +++ b/fish/fish.c
 | ||||
| @@ -476,6 +476,9 @@ main (int argc, char *argv[])
 | ||||
|    /* If we've got drives to add, add them now. */ | ||||
|    add_drives (drvs); | ||||
|   | ||||
| +  if (key_store_requires_network (ks) && guestfs_set_network (g, 1) == -1)
 | ||||
| +    exit (EXIT_FAILURE);
 | ||||
| +
 | ||||
|    /* If we've got mountpoints or prepared drives or -i option, we must | ||||
|     * launch the guest and mount them. | ||||
|     */ | ||||
| diff --git a/fuse/guestmount.c b/fuse/guestmount.c
 | ||||
| index 77c534828..3c6d57bde 100644
 | ||||
| --- a/fuse/guestmount.c
 | ||||
| +++ b/fuse/guestmount.c
 | ||||
| @@ -348,6 +348,10 @@ main (int argc, char *argv[])
 | ||||
|   | ||||
|    /* Do the guest drives and mountpoints. */ | ||||
|    add_drives (drvs); | ||||
| +
 | ||||
| +  if (key_store_requires_network (ks) && guestfs_set_network (g, 1) == -1)
 | ||||
| +    exit (EXIT_FAILURE);
 | ||||
| +
 | ||||
|    if (guestfs_launch (g) == -1) | ||||
|      exit (EXIT_FAILURE); | ||||
|    if (inspector) | ||||
| -- 
 | ||||
| 2.31.1 | ||||
| 
 | ||||
| @ -48,7 +48,7 @@ Summary:       Access and modify virtual machine disk images | ||||
| Name:          libguestfs | ||||
| Epoch:         1 | ||||
| Version:       1.48.3 | ||||
| Release:       3%{?dist} | ||||
| Release:       4%{?dist} | ||||
| License:       LGPLv2+ | ||||
| 
 | ||||
| # Build only for architectures that have a kernel | ||||
| @ -107,6 +107,13 @@ Patch0014:     0014-RHEL-Create-etc-crypto-policies-back-ends-opensslcnf.patch | ||||
| Patch0015:     0015-build-Pick-first-field-in-ID_LIKE.patch | ||||
| Patch0016:     0016-appliance-daemon-disable-lvm2-devicesfile.patch | ||||
| Patch0017:     0017-php-add-arginfo-to-php-bindings.patch | ||||
|   # *NB* I modified this patch by hand to remove references to any | ||||
|   # files in common/mlcustomize. | ||||
| Patch0018:     0018-docs-guestfs-security-document-CVE-2022-2211.patch | ||||
| Patch0019:     0019-introduce-the-clevis_luks_unlock-API.patch | ||||
|   # *NB* I modified this patch by hand to remove references to any | ||||
|   # files in common/mltools. | ||||
| Patch0020:     0020-guestfish-guestmount-enable-networking-for-key-ID-cl.patch | ||||
| 
 | ||||
| %if 0%{patches_touch_autotools} | ||||
| BuildRequires: autoconf, automake, libtool, gettext-devel | ||||
| @ -1141,7 +1148,7 @@ rm ocaml/html/.gitignore | ||||
| 
 | ||||
| 
 | ||||
| %changelog | ||||
| * Fri Jun 17 2022 Richard W.M. Jones <rjones@redhat.com> - 1:1.48.3-3 | ||||
| * Fri Jul 01 2022 Richard W.M. Jones <rjones@redhat.com> - 1:1.48.3-4 | ||||
| - Rebase to new stable branch version 1.48.3 | ||||
|   resolves: rhbz#2059285 | ||||
| - Disable 5-level page tables when using -cpu max | ||||
| @ -1159,6 +1166,10 @@ rm ocaml/html/.gitignore | ||||
|   resolves: rhbz#1965941 | ||||
| - Enable PHP bindings | ||||
|   resolves: rhbz#2097718 | ||||
| - Add support for Clevis & Tang | ||||
|   resolves: rhbz#1809453 | ||||
| - Fix CVE-2022-2211 Denial of Service in --key parameter | ||||
|   resolves: rhbz#2101281 | ||||
| 
 | ||||
| * Thu Mar 17 2022 Richard W.M. Jones <rjones@redhat.com> - 1:1.48.0-2 | ||||
| - Disable signature checking in librpm | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user