From dac85f9e02f900a074d17f949b9f0279d55d5a70 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 1 Jul 2022 16:39:24 +0100 Subject: [PATCH] Add support for Clevis & Tang resolves: rhbz#1809453 Fix CVE-2022-2211 Denial of Service in --key parameter resolves: rhbz#2101281 --- ...stfs-security-document-CVE-2022-2211.patch | 99 +++++ ...introduce-the-clevis_luks_unlock-API.patch | 252 +++++++++++ ...ount-enable-networking-for-key-ID-cl.patch | 414 ++++++++++++++++++ libguestfs.spec | 15 +- 4 files changed, 778 insertions(+), 2 deletions(-) create mode 100644 0018-docs-guestfs-security-document-CVE-2022-2211.patch create mode 100644 0019-introduce-the-clevis_luks_unlock-API.patch create mode 100644 0020-guestfish-guestmount-enable-networking-for-key-ID-cl.patch diff --git a/0018-docs-guestfs-security-document-CVE-2022-2211.patch b/0018-docs-guestfs-security-document-CVE-2022-2211.patch new file mode 100644 index 0000000..ad382e2 --- /dev/null +++ b/0018-docs-guestfs-security-document-CVE-2022-2211.patch @@ -0,0 +1,99 @@ +From fc0fd56abc2778a8473e9d421c73c9099dade4c4 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +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 +Message-Id: <20220628115418.5376-2-lersek@redhat.com> +Reviewed-by: Richard W.M. Jones +(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). + ++=head2 CVE-2022-2211 ++ ++L ++ ++The C function in F 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 element, fit into it. The ++code mistakenly uses the C macro instead of C, and therefore ++only one element is allocated before the C terminator. ++ ++Passing precisely two I<--key ID:...> options on the command line for ++the encrypted block device C causes C to overwrite the ++terminating C, leading to an out-of-bounds read in ++C, file F. ++ ++Passing more than two I<--key ID:...> options on the command line for ++the encrypted block device C causes C itself to perform ++out-of-bounds writes. The most common symptom is a crash with C ++later on. ++ ++This issue affects -- broadly speaking -- all libguestfs-based utilities ++that accept I<--key>, namely: C, C, C, ++C, C, C, C, ++C, C, C, C, ++C, C, C. ++ + =head1 SEE ALSO + + L, +-- +2.31.1 + diff --git a/0019-introduce-the-clevis_luks_unlock-API.patch b/0019-introduce-the-clevis_luks_unlock-API.patch new file mode 100644 index 0000000..4dbafee --- /dev/null +++ b/0019-introduce-the-clevis_luks_unlock-API.patch @@ -0,0 +1,252 @@ +From 17eb49e3373e46f92768bdca0733e811603bcc80 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +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 +Message-Id: <20220630122048.19335-3-lersek@redhat.com> +Reviewed-by: Richard W.M. Jones +(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 ++ ++#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 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 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 (redudancy) pins as internal nodes ++(optionally), and C pins as leaves. C 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 parameter. ++ ++This command will fail if networking has not been enabled for the ++appliance. Refer to C. ++ ++The command creates a new block device called F. ++Reads and writes to this block device are decrypted from and encrypted ++to the underlying C respectively. Close the decrypted block ++device with C. ++ ++C cannot be C<\"control\"> because that name is reserved by ++device-mapper. ++ ++If this block device contains LVM volume groups, then calling ++C with the C parameter C will make ++them visible. ++ ++Use C 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. + Then open these devices by calling L. + 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. The appliance will need ++networking enabled (refer to L) and actual ++connectivity to the Tang servers noted in the C 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 (where C is the +-string you supply to L). +-Reads and writes to this mapper device are decrypted from and +-encrypted to the underlying block device respectively. ++called F (where C is the string ++you supply to L or ++L). 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 followed by L. +-- +2.31.1 + diff --git a/0020-guestfish-guestmount-enable-networking-for-key-ID-cl.patch b/0020-guestfish-guestmount-enable-networking-for-key-ID-cl.patch new file mode 100644 index 0000000..7bb7027 --- /dev/null +++ b/0020-guestfish-guestmount-enable-networking-for-key-ID-cl.patch @@ -0,0 +1,414 @@ +From 212708dee7c5c483dd0ce76889f7e20abba7f859 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +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 +Reviewed-by: Richard W.M. Jones +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 as passphrase. + + Read the passphrase from F. + ++=item B<--key> C:clevis ++ ++Attempt passphrase-less unlocking for C with Clevis, over the ++network. Please refer to L 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 + diff --git a/libguestfs.spec b/libguestfs.spec index 7b8af8e..bbee0e2 100644 --- a/libguestfs.spec +++ b/libguestfs.spec @@ -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 - 1:1.48.3-3 +* Fri Jul 01 2022 Richard W.M. Jones - 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 - 1:1.48.0-2 - Disable signature checking in librpm