NetBSD Problem Report #58212

From mdehling@gmail.com  Sun Apr 28 20:13:09 2024
Return-Path: <mdehling@gmail.com>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
	(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 661B41A9238
	for <gnats-bugs@gnats.NetBSD.org>; Sun, 28 Apr 2024 20:13:09 +0000 (UTC)
Message-Id: <CAP+FTfX-x5FWjpGxQhUeZ5Vg+ytHqYM5aX+UzbsRhEMeg623OA@mail.gmail.com>
Date: Sun, 28 Apr 2024 13:13:05 -0700
From: Malte Dehling <mdehling@gmail.com>
To: GNATS Bugs <gnats-bugs@netbsd.org>
Subject: cgdconfig(8): Add zfs verification method
X-Send-Pr-Version: 3.95

>Number:         58212
>Category:       bin
>Synopsis:       cgdconfig(8): Add zfs verification method
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sun Apr 28 20:15:00 +0000 2024
>Last-Modified:  Sun May 12 18:05:01 +0000 2024
>Originator:     Malte Dehling
>Release:        NetBSD 10.0, -current
>Organization:
>Environment:
    NetBSD 10.0 (GENERIC) #4: Wed Apr 24 12:21:26 PDT 2024
    mdehling@nb-base-dev:/scratch/obj/sys/arch/amd64/compile/GENERIC amd64

>Description:
I would like to propose the attached patch to add a zfs verification
method to the cgdconfig(8) program.  With such a method, it will be
possible to use cgd devices as vdevs in a zpool without an intermediate
gpt.

The verification method used here is the same as in fstyp(8): read the
first vdev label (256k at offset 0) and see if vdev_label->vl_vdev_phys
(112k-40b at offset 16k) describes a valid nvlist structure with existing
zpool name property.

>How-To-Repeat:

>Fix:
Make it possible to use cgd devices as vdevs in a zpool without an
intermediate gpt.
---
 .../amd64/ramdisks/ramdisk-cgdroot/Makefile   |  3 ++
 .../amd64/ramdisks/ramdisk-cgdroot/list.zfs   |  3 ++
 rescue/Makefile                               |  4 ++
 rescue/list.zfs                               |  3 ++
 sbin/cgdconfig/Makefile                       | 18 +++++++
 sbin/cgdconfig/cgdconfig.8                    |  2 +
 sbin/cgdconfig/cgdconfig.c                    | 53 +++++++++++++++++++
 sbin/cgdconfig/params.c                       |  9 ++++
 sbin/cgdconfig/params.h                       |  1 +
 9 files changed, 96 insertions(+)
 create mode 100644 distrib/amd64/ramdisks/ramdisk-cgdroot/list.zfs
 create mode 100644 rescue/list.zfs

diff --git a/distrib/amd64/ramdisks/ramdisk-cgdroot/Makefile
b/distrib/amd64/ramdisks/ramdisk-cgdroot/Makefile
index 7be7a009f28..75b07c6ebf0 100644
--- a/distrib/amd64/ramdisks/ramdisk-cgdroot/Makefile
+++ b/distrib/amd64/ramdisks/ramdisk-cgdroot/Makefile
@@ -9,6 +9,9 @@ SMALLPROG_INET6=1
 .include "${.CURDIR}/../common/Makefile.ramdisk"

 LISTS+=		${DISTRIBDIR}/common/list.cgdroot
+.if ${MKZFS} != "no"
+LISTS+=		${.CURDIR}/list.zfs
+.endif
 MTREECONF+=	${DISTRIBDIR}/common/mtree.cgdroot

 .if ${USE_INET6} != "no"
diff --git a/distrib/amd64/ramdisks/ramdisk-cgdroot/list.zfs
b/distrib/amd64/ramdisks/ramdisk-cgdroot/list.zfs
new file mode 100644
index 00000000000..8247065d020
--- /dev/null
+++ b/distrib/amd64/ramdisks/ramdisk-cgdroot/list.zfs
@@ -0,0 +1,3 @@
+#	$NetBSD$
+
+LIBS    -lnvpair
diff --git a/rescue/Makefile b/rescue/Makefile
index 459e8a7135a..6ed6c178f3f 100644
--- a/rescue/Makefile
+++ b/rescue/Makefile
@@ -42,6 +42,10 @@ LISTS+=		${.CURDIR}/list.${f}
 LISTS+=		${.CURDIR}/list.inet6
 .endif

+.if ${MKZFS} != "no"
+LISTS+=		${.CURDIR}/list.zfs
+.endif
+
 LISTS+=		${.CURDIR}/list.crypto
 CRUNCHENV+=	MKKERBEROS=no		# for ssh

diff --git a/rescue/list.zfs b/rescue/list.zfs
new file mode 100644
index 00000000000..02bf8bc362c
--- /dev/null
+++ b/rescue/list.zfs
@@ -0,0 +1,3 @@
+#	$NetBSD$
+
+LIBS	-lnvpair
diff --git a/sbin/cgdconfig/Makefile b/sbin/cgdconfig/Makefile
index fec75b4e375..bf1be8109e3 100644
--- a/sbin/cgdconfig/Makefile
+++ b/sbin/cgdconfig/Makefile
@@ -29,4 +29,22 @@ ARGON2_NO_THREADS=1
 .include "${NETBSDSRCDIR}/external/apache2/argon2/lib/libargon2/Makefile.inc"
 .endif

+.if ${MKZFS} != "no"
+DPADD+=	${LIBNVPAIR}
+LDADD+=	-lnvpair
+CPPFLAGS+=	-DHAVE_ZFS
+
+OSNET=${NETBSDSRCDIR}/external/cddl/osnet
+CPPFLAGS.cgdconfig.c+=	-I${OSNET}/include
+CPPFLAGS.cgdconfig.c+=	-I${OSNET}/sys
+CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/head
+CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/lib/libzpool/common
+CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/uts/common
+CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/uts/common/fs/zfs
+CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/lib/libnvpair
+
+COPTS.cgdconfig.c+=	-Wno-unknown-pragmas
+COPTS.cgdconfig.c+=	-Wno-strict-prototypes
+.endif
+
 .include <bsd.prog.mk>
diff --git a/sbin/cgdconfig/cgdconfig.8 b/sbin/cgdconfig/cgdconfig.8
index 2b0da2cb732..e48ccde02c5 100644
--- a/sbin/cgdconfig/cgdconfig.8
+++ b/sbin/cgdconfig/cgdconfig.8
@@ -270,6 +270,8 @@ scan for a valid Master Boot Record.
 scan for a valid GUID partition table.
 .It ffs
 scan for a valid FFS file system.
+.It zfs
+scan for a valid ZFS vdev label (if compiled with MKZFS).
 .It re-enter
 prompt for passphrase twice, and ensure entered passphrases are
 identical.
diff --git a/sbin/cgdconfig/cgdconfig.c b/sbin/cgdconfig/cgdconfig.c
index 9a634ef37e2..4baccb1d68f 100644
--- a/sbin/cgdconfig/cgdconfig.c
+++ b/sbin/cgdconfig/cgdconfig.c
@@ -73,6 +73,11 @@ __RCSID("$NetBSD: cgdconfig.c,v 1.61 2022/11/17
06:40:38 chs Exp $");

 #include <ufs/ffs/fs.h>

+#ifdef HAVE_ZFS
+#include <libnvpair.h>
+#include <sys/vdev_impl.h>
+#endif
+
 #include "params.h"
 #include "pkcs5_pbkdf2.h"
 #include "utils.h"
@@ -170,6 +175,9 @@ static int	 verify_ffs(int);
 static int	 verify_reenter(struct params *);
 static int	 verify_mbr(int);
 static int	 verify_gpt(int);
+#ifdef HAVE_ZFS
+static int	 verify_zfs(int);
+#endif

 __dead static void	 usage(void);

@@ -1024,6 +1032,10 @@ verify(struct params *p, int fd)
 		return verify_mbr(fd);
 	case VERIFY_GPT:
 		return verify_gpt(fd);
+#ifdef HAVE_ZFS
+	case VERIFY_ZFS:
+		return verify_zfs(fd);
+#endif
 	default:
 		warnx("unimplemented verification method");
 		return -1;
@@ -1182,6 +1194,47 @@ verify_gpt(int fd)
 	return ret;
 }

+#ifdef HAVE_ZFS
+static int
+verify_zfs(int fd)
+{
+	vdev_label_t *vdev_label;
+	vdev_phys_t *vdev_phys;
+	nvlist_t *config = NULL;
+	ssize_t ret;
+
+	/*
+	 * Read the first ZFS vdev label located at offset 0.
+	 */
+	vdev_label = emalloc(sizeof *vdev_label);
+	ret = prog_pread(fd, vdev_label, sizeof(*vdev_label), 0);
+	if (ret < 0) {
+		warn("pread");
+		ret = 1;
+		goto bail;
+	} else if ((size_t)ret < sizeof(*vdev_label)) {
+		warnx("pread: incomplete block");
+		ret = 1;
+		goto bail;
+	};
+
+	ret = 0;
+
+	vdev_phys = &(vdev_label->vl_vdev_phys);
+	if ((nvlist_unpack(vdev_phys->vp_nvlist,
+		sizeof(vdev_phys->vp_nvlist), &config, 0)) != 0 ||
+		!nvlist_exists(config, "name"))
+	{
+		ret = 1;
+	};
+
+	nvlist_free(config);
+ bail:
+	free(vdev_label);
+	return ret;
+}
+#endif
+
 static off_t sblock_try[] = SBLOCKSEARCH;

 static int
diff --git a/sbin/cgdconfig/params.c b/sbin/cgdconfig/params.c
index 91af02c21cc..5da25ec0595 100644
--- a/sbin/cgdconfig/params.c
+++ b/sbin/cgdconfig/params.c
@@ -287,6 +287,10 @@ params_verify_method(string_t *in)
 		p->verify_method = VERIFY_MBR;
 	if (!strcmp("gpt", vm))
 		p->verify_method = VERIFY_GPT;
+#ifdef HAVE_ZFS
+	if (!strcmp("zfs", vm))
+		p->verify_method = VERIFY_ZFS;
+#endif

 	string_free(in);

@@ -1065,6 +1069,11 @@ params_fput(struct params *p, FILE *f)
 	case VERIFY_GPT:
 		print_kvpair_cstr(f, ts, "verify_method", "gpt");
 		break;
+#ifdef HAVE_ZFS
+	case VERIFY_ZFS:
+		print_kvpair_cstr(f, ts, "verify_method", "zfs");
+		break;
+#endif
 	default:
 		warnx("unsupported verify_method (%d)", p->verify_method);
 		return -1;
diff --git a/sbin/cgdconfig/params.h b/sbin/cgdconfig/params.h
index fb79a0deb80..ef49dde6aba 100644
--- a/sbin/cgdconfig/params.h
+++ b/sbin/cgdconfig/params.h
@@ -81,6 +81,7 @@ struct params {
 #define VERIFY_REENTER		0x4
 #define VERIFY_MBR      	0x5
 #define VERIFY_GPT      	0x6
+#define VERIFY_ZFS      	0x7

 /* shared key derivation methods */

-- 
Malte Dehling

>Audit-Trail:
From: Taylor R Campbell <riastradh@NetBSD.org>
To: Malte Dehling <mdehling@gmail.com>
Cc: gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Subject: Re: bin/58212: cgdconfig(8): Add zfs verification method
Date: Sun, 28 Apr 2024 20:38:55 +0000

 Cool, thanks!  We should definitely have a zfs verification method for
 cgdconfig(8).  But I think we can do it a little more robustly -- and
 with fewer dependencies outside libc in cgdconfig(8).

 Here's what I wrote the last time someone suggested this -- identical
 to the nvlist approach you adapted from fstyp(8) -- but my round tuit
 supply ran short of taking the approach I suggested of verifying a
 fixed magic number and a SHA-256 hash over the header:


 I wonder if we can do this without pulling so much into cgdconfig and
 doing parsing there, though?

 A comment in fstyp(8) suggests there's a checksum (which it doesn't
 verify); maybe we can use that?

 Would be nice if there were a reference to the data format that we can
 put in a comment -- which should appear in fstyp(8) too.

 Would also be nice if we could quantify the false acceptance
 probability under uniform random data (i.e., wrong password, modelling
 password hash as uniform random function).  For the current
 verification methods it looks like:

 verify_ffs      =3D 1 - (1 - 4/2^32)^4 < one in 250 * 10^6
 verify_gpt      < 1 - (1 - 1/2^96)^4 < one in 10^28
 verify_mbr      =3D 1/2^16 (much too high for my comfort!)

 For zfs I would hope it can be much lower than for ffs.

 I went hunting through the zfs header files and it looks like the vdev
 label has a magic number and some kind of 256-bit checksum -- not
 specific to the vdev label (other zpool data structures have the same
 magic number and checksum), but maybe we can use it:

 /* vdev_impl.h */
 typedef struct vdev_phys {
         char            vp_nvlist[VDEV_PHYS_SIZE - sizeof (zio_eck_t)];
         zio_eck_t       vp_zbt;
 } vdev_phys_t;

 typedef struct vdev_label {
         char            vl_pad1[VDEV_PAD_SIZE];                 /*  8K */
         char            vl_pad2[VDEV_PAD_SIZE];                 /*  8K */
         vdev_phys_t     vl_vdev_phys;                           /* 112K */
         char            vl_uberblock[VDEV_UBERBLOCK_RING];      /* 128K */
 } vdev_label_t;                                                 /* 256K tot=
 al */

 /* zio.h */
 #define ZEC_MAGIC       0x210da7ab10c7a11ULL

 typedef struct zio_eck {
         uint64_t        zec_magic;      /* for validation, endianness   */
         zio_cksum_t     zec_cksum;      /* 256-bit checksum             */
 } zio_eck_t;

 Judging by zio_checksum.c, it looks like it's SHA-256.

 I grepped for some of the nvlist keys that zfs uses, and found
 libzfs_import.c, which does a couple things:

 - Tries not just offset 0, but up to four different offsets with the
   label_offset function.

 - Checks for "state" and "txg" in the nvlist.

 vdev_label_read_config also tries several different offsets, but only
 checks for "txg" -- and doesn't even require that.  So the nvlist keys
 may be a red herring here.

 But with the checksum, it looks like we could get by with:

 1. (optional) for each possible label offset:

 2. verify vl->vl_vdev_phys.vp_zbt.zec_magic =3D=3D ZEC_MAGIC or
    byte-swapped, and

 3. verify vl->vl_vdev_phys.vp_zbt.zec_cksum is the SHA-256 hash of
    whatever part of vl it's computed over.

 The SHA-256 hash appears to be calculated over vl->vl_vdev_phys, but
 with vp_zbt.zec_cksum set to

    {labeloffset + offsetof(struct vdev_label, vl_vdev_phys), 0, 0, 0},

 with the 64-bit words possibly encoded in the same byte order as
 zec_magic.  Except then the bytes of each 64-bit word in the SHA-256
 output hash are byte-swapped.

 This seems to check out on the one sample I tried, and it should have
 a false detection probability under uniform random data well below
 1/2^256 which is extremely comfortable.  (Should also have low false
 detection probability under other formats too because of the checksum
 even if you store a file with the magic number in it.)

From: Robert Elz <kre@munnari.OZ.AU>
To: Taylor R Campbell <riastradh@NetBSD.org>
Cc: Malte Dehling <mdehling@gmail.com>, gnats-bugs@NetBSD.org,
        netbsd-bugs@NetBSD.org
Subject: Re: bin/58212: cgdconfig(8): Add zfs verification method
Date: Mon, 29 Apr 2024 05:19:05 +0700

     Date:        Sun, 28 Apr 2024 20:38:55 +0000
     From:        Taylor R Campbell <riastradh@NetBSD.org>
     Message-ID:  <20240428203857.A5B9C84D67@mail.netbsd.org>

   | A comment in fstyp(8) suggests there's a checksum (which it doesn't
   | verify); maybe we can use that?

 As long as the checksum is guaranteed to be there (if it is currently
 unused, someone might decide not to bother calculating it, one day)
 and it is clear over which bytes the checksum is calculated (and
 which algorithm to use), that would be fine.

   | Would also be nice if we could quantify the false acceptance probability

 It doesn't really matter, this has nothing whatever to do with the
 security of the cgd, these validity checks are used merely to allow
 detection of incorrect passwords (etc) - they (even when they appear
 to be validating some data struct) have nothing whatever to do with
 use of any of the data.

 That is, if the validation method should give a false positive, the
 only effect is that when the cgd data is later used, it will effectively
 contain rubbish (just as would, say, an uninitialised partition of
 a drive) - if the (software) user of the data cannot detect that
 the daya is invalid, then it has problems, but they aren't caused
 by cgd, it would have the same problems in other uses.

 This interpretation is reinforced by the fact that it is possible to
 simply disable the verification (command line option), not bother doing
 it at all, and everything still works, and is just as secure.

 It is nice for the (human) user to be told what the issue is, as
 soon as possible (eg: to be told the wrong password has been given)
 rather than have some later step complain, but it isn't important.

 The 1/2^16 probability that the MBR gives (assuming you're correct
 about that, which I don't doubt) is really good enough - the much
 higher probabilities against false positivies of the other methods
 are nice, I guess, but aren't there because of cgd, they're what
 happened to exist anyway.

 kre

From: Malte Dehling <mdehling@gmail.com>
To: Taylor R Campbell <riastradh@netbsd.org>
Cc: gnats-bugs@netbsd.org, netbsd-bugs@netbsd.org
Subject: Re: Re: bin/58212: cgdconfig(8): Add zfs verification method
Date: Sun, 28 Apr 2024 16:07:27 -0700

 --00000000000069a3400617303387
 Content-Type: text/plain; charset="UTF-8"

 Hi Taylor,

 On Sun, Apr 28, 2024 at 08:38:55PM +0000, Taylor R Campbell wrote:
 > Cool, thanks!  We should definitely have a zfs verification method for
 > cgdconfig(8).  But I think we can do it a little more robustly -- and
 > with fewer dependencies outside libc in cgdconfig(8).
 >
 > Here's what I wrote the last time someone suggested this -- identical
 > to the nvlist approach you adapted from fstyp(8) -- but my round tuit
 > supply ran short of taking the approach I suggested of verifying a
 > fixed magic number and a SHA-256 hash over the header:
 >
 > I wonder if we can do this without pulling so much into cgdconfig and
 > doing parsing there, though?
 >
 > A comment in fstyp(8) suggests there's a checksum (which it doesn't
 > verify); maybe we can use that?
 >
 > Would be nice if there were a reference to the data format that we can
 > put in a comment -- which should appear in fstyp(8) too.
 >
 > Would also be nice if we could quantify the false acceptance
 > probability under uniform random data (i.e., wrong password, modelling
 > password hash as uniform random function).  For the current
 > verification methods it looks like:
 >
 > verify_ffs      = 1 - (1 - 4/2^32)^4 < one in 250 * 10^6
 > verify_gpt      < 1 - (1 - 1/2^96)^4 < one in 10^28
 > verify_mbr      = 1/2^16 (much too high for my comfort!)
 >
 > For zfs I would hope it can be much lower than for ffs.
 >
 > I went hunting through the zfs header files and it looks like the vdev
 > label has a magic number and some kind of 256-bit checksum -- not
 > specific to the vdev label (other zpool data structures have the same
 > magic number and checksum), but maybe we can use it:
 >
 > /* vdev_impl.h */
 > typedef struct vdev_phys {
 >         char            vp_nvlist[VDEV_PHYS_SIZE - sizeof (zio_eck_t)];
 >         zio_eck_t       vp_zbt;
 > } vdev_phys_t;
 >
 > typedef struct vdev_label {
 >         char            vl_pad1[VDEV_PAD_SIZE];                 /*  8K */
 >         char            vl_pad2[VDEV_PAD_SIZE];                 /*  8K */
 >         vdev_phys_t     vl_vdev_phys;                           /* 112K */
 >         char            vl_uberblock[VDEV_UBERBLOCK_RING];      /* 128K */
 > } vdev_label_t;                                                 /* 256K total */
 >
 > /* zio.h */
 > #define ZEC_MAGIC       0x210da7ab10c7a11ULL
 >
 > typedef struct zio_eck {
 >         uint64_t        zec_magic;      /* for validation, endianness   */
 >         zio_cksum_t     zec_cksum;      /* 256-bit checksum             */
 > } zio_eck_t;
 >
 > Judging by zio_checksum.c, it looks like it's SHA-256.
 >
 > I grepped for some of the nvlist keys that zfs uses, and found
 > libzfs_import.c, which does a couple things:
 >
 > - Tries not just offset 0, but up to four different offsets with the
 >   label_offset function.
 >
 > - Checks for "state" and "txg" in the nvlist.
 >
 > vdev_label_read_config also tries several different offsets, but only
 > checks for "txg" -- and doesn't even require that.  So the nvlist keys
 > may be a red herring here.
 >
 > But with the checksum, it looks like we could get by with:
 >
 > 1. (optional) for each possible label offset:
 >
 > 2. verify vl->vl_vdev_phys.vp_zbt.zec_magic == ZEC_MAGIC or
 >    byte-swapped, and
 >
 > 3. verify vl->vl_vdev_phys.vp_zbt.zec_cksum is the SHA-256 hash of
 >    whatever part of vl it's computed over.
 >
 > The SHA-256 hash appears to be calculated over vl->vl_vdev_phys, but
 > with vp_zbt.zec_cksum set to
 >
 >    {labeloffset + offsetof(struct vdev_label, vl_vdev_phys), 0, 0, 0},
 >
 > with the 64-bit words possibly encoded in the same byte order as
 > zec_magic.  Except then the bytes of each 64-bit word in the SHA-256
 > output hash are byte-swapped.
 >
 > This seems to check out on the one sample I tried, and it should have
 > a false detection probability under uniform random data well below
 > 1/2^256 which is extremely comfortable.  (Should also have low false
 > detection probability under other formats too because of the checksum
 > even if you store a file with the magic number in it.)

 Thanks for the detailed response!

 I did the same reading to try and find out if there is a simple checksum
 verification function but didn't find anything.  It seems like zpool
 import never actually verifies the checksums or magic bytes in
 vl->vl_vdev_phys->vp_zbt .  There are VDEV_LABELS (4) labels, two at the
 beginning of the disk (offset 0 and 256k) and two at the end (at -512k,
 -256k) and even with all of their vl->vl_vdev_phys->vp_zbt structures
 zeroed out, zpool import shows no complaints.  The checksums are updated
 on export.

 I did a quick test and computed sha256 for vl->vdev_phys and it works as
 you described: the magic bytes need to be set first (byte-swapped on my
 system) and the offset encoded in the first of the 64bit checksum words
 (also byte-swapped).  The computed sha256 checksum is then stored as 4
 64bit words in byte-swapped order.

 Implementing the checksum verification is easy and allows us to get rid
 of the libnvpair dependency, so I don't mind implementing it that way.
 Since I haven't looked into exactly how much checking libnvpair does, I
 don't know what the risk of false positives for the current proposal is.

 Cheers,
 -- 
 Malte Dehling

 --00000000000069a3400617303387
 Content-Type: application/pgp-signature; name="signature.asc"
 Content-Disposition: attachment; filename="signature.asc"
 Content-Transfer-Encoding: base64
 X-Attachment-Id: 7279cfb2c4807360_0.1

 LS0tLS1CRUdJTiBQR1AgU0lHTkFUVVJFLS0tLS0KCmlRSXpCQUFCQ0FBZEZpRUVva3ZsOUFOMEFo
 T05jQXdlSzhMazZxS3dkMFFGQW1ZdTFxY0FDZ2tRSzhMazZxS3cKZDBUTi9nLy9Va3lYdGZqc2pF
 S0p2OGFRQ3VETHIrWHFYRVNnaEhkZHo2Rk9OdFQxaE9Wbnl0RVkvUWFOWWNMNgpoc2JEdDAvMmND
 S1JpTFFTL1lmVExtdmRkMm43S1E5V3VHbGtGcXBjZXFyZXR3RHJOTXFTN3RNcGI5eXE2VTR3Ck1D
 T1ZEZDlMTmdONllwdjF1cWZ5S3RyTzRXTERRLzJiMUZTN0F4N0VDb2ZLUGphajNVemtUUlJmU3FZ
 TkZxVEIKUzBVbXVsN0pnU25lRnhGZnRJVG9sZUQxUWZYSFRFSGpCTlN4YkVEak00cmpHbXVuMU5n
 QlJqNjRmaS9WZVZxcQpWYURjOCt5Ly9pOFUxcE9EUjZTVjkwMDQyblEreHN3TzB5Q1pwZzlXT3Y5
 QXNST1hKbTJoNTFHNmxIS0tNM25DCnR0K3p0L294UHFQY2hHZTl5dXhra05oZkdWZlJUWFl2aUJZ
 Q1ptVkxvUWl4OExDU2l6UzNMTHpjMm9rcHdLK2cKSFlmd0dqRENRK20wNXhOUEZGNWcvSTdwd0R4
 ZU8xVGdWL0Z3b1IzMHhCZUQzMEkxaVlLcDRPZjFWOWorZGNDSApNQjdwbm45ZmFzbzM4QllmVG1L
 OUNXOEZWWDNxQWJCZFhGNFoxcmlyU2gvME5yV0ViSDBVaGRISldPRFFCSFcxClJJajJvS3ZLRFV5
 N2pqT09oUFF4N3RhSUU0WWxSMVRmS1g4dHhKTXVydnY1TVZvNmpveEZPdmRjaGpYaytnVG4KRlFm
 QUhnZ3poQXZEdS95VmhubTgydzd1amNlRmVHbUxjdm5xcWVzMkxrWVFBbDEzYVhiY0JQWVRZUE1t
 V1B3QwpoQ28zeHJVQlNyU1FUVzdwWVZhcVBnRysybXAycXlxdzk5d1I3WHJ2N1Q3SVBPV1NDUzg9
 Cj1YZ092Ci0tLS0tRU5EIFBHUCBTSUdOQVRVUkUtLS0tLQo=
 --00000000000069a3400617303387--

From: Taylor R Campbell <riastradh@NetBSD.org>
To: Robert Elz <kre@munnari.OZ.AU>
Cc: Malte Dehling <mdehling@gmail.com>, gnats-bugs@NetBSD.org, netbsd-bugs@NetBSD.org
Subject: Re: bin/58212: cgdconfig(8): Add zfs verification method
Date: Mon, 29 Apr 2024 01:54:21 +0000

 > Date: Mon, 29 Apr 2024 05:19:05 +0700
 > From: Robert Elz <kre@munnari.OZ.AU>
 >=20
 >   | Would also be nice if we could quantify the false acceptance probabil=
 ity
 >=20
 > It doesn't really matter, this has nothing whatever to do with the
 > security of the cgd, these validity checks are used merely to allow
 > detection of incorrect passwords (etc) - they (even when they appear
 > to be validating some data struct) have nothing whatever to do with
 > use of any of the data.

 You're right that it's not relevant to the security of cgd's
 confidentiality against an adversary, but it does matter to users.

 Seeing `verification failed, please reenter passphrase' is unalarming.

 Seeing a host of fsck failures at boot time because the block device
 is garbage when decrypted with the wrong key is extremely alarming,
 and suggests you have faulty hardware and may have lost data.

 And if you do anything to write data back to the block device -- e.g.,
 you decide to give up on data recovery and newfs it -- you'll probably
 never get your _new_ data back because you have to make the same typo
 when you next boot.

 > The 1/2^16 probability that the MBR gives (assuming you're correct
 > about that, which I don't doubt)

 For those curious, this came by modelling the plaintext under the
 wrong key as independent uniform random bits, under which the
 verify_mbr criterion of checking for a single 16-bit magic number has
 probability 1/2^16 of passing:

 https://nxr.netbsd.org/xref/src/sbin/cgdconfig/cgdconfig.c?r=3D1.62#1058

    1070 	ret =3D prog_pread(fd, buf, SCANSIZE, 0);
    1071 	if (ret < 0) {
    1072 		warn("can't read mbr area");
    1073 		return -1;
    1074 	}
    1075=20
    1076 	memcpy(&mbr, buf, sizeof(mbr));
    1077 	if (le16toh(mbr.mbr_magic) !=3D MBR_MAGIC)
    1078 		return 1;
    1079=20
    1080 	return 0;

 The verify_ffs criterion is a little more involved, and since I wrote
 the previous message (a couple years ago) it has grown a higher false
 acceptance probability now that it allows FS_UFS2EA_MAGIC or
 FS_UFS2EA_MAGIC_SWAPPED too, so it's 1 - (1 - 6/2^32)^4 or a little
 under one in 178 * 10^6.  This arises from allowing any of six
 different 32-bit magic numbers in any of four different superblock
 locations.

 https://nxr.netbsd.org/xref/src/sbin/cgdconfig/cgdconfig.c?r=3D1.62#1185
 https://nxr.netbsd.org/xref/src/sys/ufs/ffs/fs.h?r=3D1.71#103

 >                                  is really good enough - the much
 > higher probabilities against false positivies of the other methods
 > are nice, I guess, but aren't there because of cgd, they're what
 > happened to exist anyway.

 The independent uniform random bits model is substantively different
 from usual hardware errors, where typically we model each bit as
 having an independent small (not fair coin toss) probability of error,
 or a burst of errors in a nearby region.  And human-designed data
 formats tend not to be totally randomized.  So this really is related
 to cgd and not just to the reliability of the MBR magic number in
 non-randomized applications.

 For a sense of scale, suppose there are 1000 users, and over the
 course of time, each of them types a wrong password 50 times with the
 verify_mbr method.

 In this scenario, there's better than a coin toss's odds in this
 scenario that one of those wrong password entries will be accepted,
 leading to alarming consequences when the resulting disk is full of
 gibberish.

 Maybe that's an optimistic estimate of the number of cgd(4) users, or
 a pessimistic estimate of their typing ability, but it's not _that_
 optimistic or pessimistic.  (I'm sure I've typed my password wrong
 more than 50 times.)

From: Malte Dehling <mdehling@gmail.com>
To: netbsd-bugs@netbsd.org
Cc: Taylor R Campbell <riastradh@netbsd.org>, gnats-bugs@netbsd.org
Subject: Re: Re: Re: bin/58212: cgdconfig(8): Add zfs verification method
Date: Mon, 29 Apr 2024 18:46:11 -0700

 Hi,

 On Sun, Apr 28, 2024 at 01:13:05PM -0700, Malte Dehling wrote:
 > >Submitter-Id:  net
 > >Originator:    Malte Dehling
 > >Organization:
 > >Confidential:  no
 > >Synopsis:      cgdconfig(8): Add zfs verification method
 > >Severity:      non-critical
 > >Priority:      medium
 > >Category:      bin
 > >Class:         change-request
 > >Release:       NetBSD 10.0, -current
 > >Environment:
 >     NetBSD 10.0 (GENERIC) #4: Wed Apr 24 12:21:26 PDT 2024
 >     mdehling@nb-base-dev:/scratch/obj/sys/arch/amd64/compile/GENERIC amd64
 >
 > >Description:
 > I would like to propose the attached patch to add a zfs verification
 > method to the cgdconfig(8) program.  With such a method, it will be
 > possible to use cgd devices as vdevs in a zpool without an intermediate
 > gpt.

 How about this version instead?

 I _think_ I got the byte order logic right:
 - deduce the byte order from the magic bytes and use it to initialize
   the checksum computation with the same byte order
 - on-disk sha256 is always stored in big-endian order, so convert
   whatever we compute to BE before comparing

 Tested only on LE since I don't have a BE netbsd system handy right now.


 ---
 Implement vdev label checksum verification.  Allows using raw cgd
 devices as vdevs in a zpool.
 ---
  sbin/cgdconfig/Makefile    |  15 +++++
  sbin/cgdconfig/cgdconfig.8 |   2 +
  sbin/cgdconfig/cgdconfig.c | 111 +++++++++++++++++++++++++++++++++++++
  sbin/cgdconfig/params.c    |   9 +++
  sbin/cgdconfig/params.h    |   1 +
  5 files changed, 138 insertions(+)

 diff --git a/sbin/cgdconfig/Makefile b/sbin/cgdconfig/Makefile
 index fec75b4e375..0ed669ab738 100644
 --- a/sbin/cgdconfig/Makefile
 +++ b/sbin/cgdconfig/Makefile
 @@ -29,4 +29,19 @@ ARGON2_NO_THREADS=1
  .include "${NETBSDSRCDIR}/external/apache2/argon2/lib/libargon2/Makefile.inc"
  .endif

 +.if ${MKZFS} != "no"
 +CPPFLAGS+=	-DHAVE_ZFS
 +
 +OSNET=${NETBSDSRCDIR}/external/cddl/osnet
 +CPPFLAGS.cgdconfig.c+=	-I${OSNET}/include
 +CPPFLAGS.cgdconfig.c+=	-I${OSNET}/sys
 +CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/head
 +CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/lib/libzpool/common
 +CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/uts/common
 +CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/uts/common/fs/zfs
 +
 +COPTS.cgdconfig.c+=	-Wno-unknown-pragmas
 +COPTS.cgdconfig.c+=	-Wno-strict-prototypes
 +.endif
 +
  .include <bsd.prog.mk>
 diff --git a/sbin/cgdconfig/cgdconfig.8 b/sbin/cgdconfig/cgdconfig.8
 index 2b0da2cb732..e48ccde02c5 100644
 --- a/sbin/cgdconfig/cgdconfig.8
 +++ b/sbin/cgdconfig/cgdconfig.8
 @@ -270,6 +270,8 @@ scan for a valid Master Boot Record.
  scan for a valid GUID partition table.
  .It ffs
  scan for a valid FFS file system.
 +.It zfs
 +scan for a valid ZFS vdev label (if compiled with MKZFS).
  .It re-enter
  prompt for passphrase twice, and ensure entered passphrases are
  identical.
 diff --git a/sbin/cgdconfig/cgdconfig.c b/sbin/cgdconfig/cgdconfig.c
 index 9a634ef37e2..de4d09013bf 100644
 --- a/sbin/cgdconfig/cgdconfig.c
 +++ b/sbin/cgdconfig/cgdconfig.c
 @@ -73,6 +73,11 @@ __RCSID("$NetBSD: cgdconfig.c,v 1.61 2022/11/17
 06:40:38 chs Exp $");

  #include <ufs/ffs/fs.h>

 +#ifdef HAVE_ZFS
 +#include <sys/vdev_impl.h>
 +#include <sha2.h>
 +#endif
 +
  #include "params.h"
  #include "pkcs5_pbkdf2.h"
  #include "utils.h"
 @@ -170,6 +175,9 @@ static int	 verify_ffs(int);
  static int	 verify_reenter(struct params *);
  static int	 verify_mbr(int);
  static int	 verify_gpt(int);
 +#ifdef HAVE_ZFS
 +static int	 verify_zfs(int);
 +#endif

  __dead static void	 usage(void);

 @@ -1024,6 +1032,10 @@ verify(struct params *p, int fd)
  		return verify_mbr(fd);
  	case VERIFY_GPT:
  		return verify_gpt(fd);
 +#ifdef HAVE_ZFS
 +	case VERIFY_ZFS:
 +		return verify_zfs(fd);
 +#endif
  	default:
  		warnx("unimplemented verification method");
  		return -1;
 @@ -1182,6 +1194,105 @@ verify_gpt(int fd)
  	return ret;
  }

 +#ifdef HAVE_ZFS
 +
 +#define ZIO_CHECKSUM_BE(zcp)					\
 +{								\
 +	(zcp)->zc_word[0] = BE_64((zcp)->zc_word[0]);		\
 +	(zcp)->zc_word[1] = BE_64((zcp)->zc_word[1]);		\
 +	(zcp)->zc_word[2] = BE_64((zcp)->zc_word[2]);		\
 +	(zcp)->zc_word[3] = BE_64((zcp)->zc_word[3]);		\
 +}
 +
 +static int
 +verify_zfs(int fd)
 +{
 +	struct stat sb;
 +	vdev_phys_t *vdev_phys;
 +	off_t vdev_phys_off;
 +	ssize_t ret;
 +	bool byteswap;
 +	zio_cksum_t cksum_found, cksum_real;
 +	SHA256_CTX ctx;
 +
 +	if (fstat(fd, &sb)) {
 +		warn("fstat");
 +		return 1;
 +	} else if (sb.st_size == 0) {
 +		warnx("fstat returned size zero");
 +		return 1;
 +	}
 +
 +	vdev_phys = emalloc(sizeof *vdev_phys);
 +	for (int l = 0; l < VDEV_LABELS; l++) {
 +		vdev_phys_off = ( l < VDEV_LABELS/2 ?
 +		    l * sizeof(vdev_label_t) :
 +		    sb.st_size - (VDEV_LABELS-l) * sizeof(vdev_label_t) ) \
 +		    + offsetof(vdev_label_t, vl_vdev_phys);
 +
 +		ret = prog_pread(fd, vdev_phys, sizeof *vdev_phys,
 +		    vdev_phys_off);
 +		if (ret < 0) {
 +			warn("pread");
 +			ret = 1;
 +			break;
 +		} else if ((size_t)ret < sizeof *vdev_phys) {
 +			warnx("pread: incomplete block");
 +			ret = 1;
 +			break;
 +		}
 +
 +		ret = 0;
 +
 +		if (vdev_phys->vp_zbt.zec_magic == BSWAP_64(ZEC_MAGIC))
 +			byteswap = true;
 +		else if (vdev_phys->vp_zbt.zec_magic == ZEC_MAGIC)
 +			byteswap = false;
 +		else {
 +			ret = 1;
 +			break;
 +		};
 +
 +		cksum_found = vdev_phys->vp_zbt.zec_cksum;
 +
 +		ZIO_SET_CHECKSUM(&vdev_phys->vp_zbt.zec_cksum,
 +		    vdev_phys_off, 0, 0, 0);
 +		if (byteswap)
 +			ZIO_CHECKSUM_BSWAP(&vdev_phys->vp_zbt.zec_cksum);
 +
 +		SHA256Init(&ctx);
 +		SHA256Update(&ctx, (uint8_t *)vdev_phys, sizeof *vdev_phys);
 +		SHA256Final(&cksum_real, &ctx);
 +
 +		/*
 +		 * For historical reasons the on-disk sha256 checksums are
 +		 * always in big endian format.
 +		 * (see cddl/osnet/dist/uts/common/fs/zfs/sha256.c)
 +		 */
 +		ZIO_CHECKSUM_BE(&cksum_real);
 +
 +		if (!ZIO_CHECKSUM_EQUAL(cksum_found, cksum_real)) {
 +			warnx("checksum mismatch on vdev label %d", l);
 +			warnx("found %lx, %lx, %lx, %lx",
 +			    (unsigned long)cksum_found.zc_word[0],
 +			    (unsigned long)cksum_found.zc_word[1],
 +			    (unsigned long)cksum_found.zc_word[2],
 +			    (unsigned long)cksum_found.zc_word[3]);
 +			warnx("expected %lx, %lx, %lx, %lx",
 +			    (unsigned long)cksum_real.zc_word[0],
 +			    (unsigned long)cksum_real.zc_word[1],
 +			    (unsigned long)cksum_real.zc_word[2],
 +			    (unsigned long)cksum_real.zc_word[3]);
 +			ret = 1;
 +			break;
 +		}
 +	}
 +	free(vdev_phys);
 +	return ret;
 +}
 +
 +#endif
 +
  static off_t sblock_try[] = SBLOCKSEARCH;

  static int
 diff --git a/sbin/cgdconfig/params.c b/sbin/cgdconfig/params.c
 index 91af02c21cc..5da25ec0595 100644
 --- a/sbin/cgdconfig/params.c
 +++ b/sbin/cgdconfig/params.c
 @@ -287,6 +287,10 @@ params_verify_method(string_t *in)
  		p->verify_method = VERIFY_MBR;
  	if (!strcmp("gpt", vm))
  		p->verify_method = VERIFY_GPT;
 +#ifdef HAVE_ZFS
 +	if (!strcmp("zfs", vm))
 +		p->verify_method = VERIFY_ZFS;
 +#endif

  	string_free(in);

 @@ -1065,6 +1069,11 @@ params_fput(struct params *p, FILE *f)
  	case VERIFY_GPT:
  		print_kvpair_cstr(f, ts, "verify_method", "gpt");
  		break;
 +#ifdef HAVE_ZFS
 +	case VERIFY_ZFS:
 +		print_kvpair_cstr(f, ts, "verify_method", "zfs");
 +		break;
 +#endif
  	default:
  		warnx("unsupported verify_method (%d)", p->verify_method);
  		return -1;
 diff --git a/sbin/cgdconfig/params.h b/sbin/cgdconfig/params.h
 index fb79a0deb80..ef49dde6aba 100644
 --- a/sbin/cgdconfig/params.h
 +++ b/sbin/cgdconfig/params.h
 @@ -81,6 +81,7 @@ struct params {
  #define VERIFY_REENTER		0x4
  #define VERIFY_MBR      	0x5
  #define VERIFY_GPT      	0x6
 +#define VERIFY_ZFS      	0x7

  /* shared key derivation methods */

 -- 
 2.44.0


 Cheers,
 -- 
 Malte Dehling

From: Malte Dehling <mdehling@gmail.com>
To: netbsd-bugs@netbsd.org
Cc: Taylor R Campbell <riastradh@netbsd.org>, gnats-bugs@netbsd.org
Subject: Re: Re: Re: Re: bin/58212: cgdconfig(8): Add zfs verification method
Date: Sat, 4 May 2024 18:42:57 -0400

 Hi again,

 On Mon, Apr 29, 2024 at 02:59:05PM -0700, Malte Dehling wrote:
 > On Sun, Apr 28, 2024 at 01:13:05PM -0700, Malte Dehling wrote:
 > > >Submitter-Id:  net
 > > >Originator:    Malte Dehling
 > > >Organization:
 > > >Confidential:  no
 > > >Synopsis:      cgdconfig(8): Add zfs verification method
 > > >Severity:      non-critical
 > > >Priority:      medium
 > > >Category:      bin
 > > >Class:         change-request
 > > >Release:       NetBSD 10.0, -current
 > > >Environment:
 > >     NetBSD 10.0 (GENERIC) #4: Wed Apr 24 12:21:26 PDT 2024
 > >     mdehling@nb-base-dev:/scratch/obj/sys/arch/amd64/compile/GENERIC amd64
 > >
 > > >Description:
 > > I would like to propose the attached patch to add a zfs verification
 > > method to the cgdconfig(8) program.  With such a method, it will be
 > > possible to use cgd devices as vdevs in a zpool without an intermediate
 > > gpt.
 >
 > How about this version instead?
 >
 > I _think_ I got the byte order logic right:
 > - deduce the byte order from the magic bytes and use it to initialize
 >   the checksum computation with the same byte order
 > - on-disk sha256 is always stored in big-endian order, so convert
 >   whatever we compute to BE before comparing
 >
 > Tested only on LE since I don't have a BE netbsd system handy right now.

 Here's an updated version of the patch.

 I fixed two issues:
 - my attempt to read the vdev size using fstat didn't work, so instead
   this uses ioctl(fd, DIOCGMEDIASIZE, &vdev_size)
 - the checksum is now correctly byte-swapped based on the endianness of
   the magic number

 I've tested this version on amd64 (LE) and aarch64be (BE) separately,
 and by passing zpool on cgd disk images between them.

 ---
 Implement vdev label checksum verification.  Allows using raw cgd
 devices as vdevs in a zpool.
 ---
  sbin/cgdconfig/Makefile    |  15 +++++
  sbin/cgdconfig/cgdconfig.8 |   2 +
  sbin/cgdconfig/cgdconfig.c | 109 +++++++++++++++++++++++++++++++++++++
  sbin/cgdconfig/params.c    |   9 +++
  sbin/cgdconfig/params.h    |   1 +
  5 files changed, 136 insertions(+)

 diff --git a/sbin/cgdconfig/Makefile b/sbin/cgdconfig/Makefile
 index fec75b4e375..0ed669ab738 100644
 --- a/sbin/cgdconfig/Makefile
 +++ b/sbin/cgdconfig/Makefile
 @@ -29,4 +29,19 @@ ARGON2_NO_THREADS=1
  .include "${NETBSDSRCDIR}/external/apache2/argon2/lib/libargon2/Makefile.inc"
  .endif

 +.if ${MKZFS} != "no"
 +CPPFLAGS+=	-DHAVE_ZFS
 +
 +OSNET=${NETBSDSRCDIR}/external/cddl/osnet
 +CPPFLAGS.cgdconfig.c+=	-I${OSNET}/include
 +CPPFLAGS.cgdconfig.c+=	-I${OSNET}/sys
 +CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/head
 +CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/lib/libzpool/common
 +CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/uts/common
 +CPPFLAGS.cgdconfig.c+=	-I${OSNET}/dist/uts/common/fs/zfs
 +
 +COPTS.cgdconfig.c+=	-Wno-unknown-pragmas
 +COPTS.cgdconfig.c+=	-Wno-strict-prototypes
 +.endif
 +
  .include <bsd.prog.mk>
 diff --git a/sbin/cgdconfig/cgdconfig.8 b/sbin/cgdconfig/cgdconfig.8
 index 2b0da2cb732..e48ccde02c5 100644
 --- a/sbin/cgdconfig/cgdconfig.8
 +++ b/sbin/cgdconfig/cgdconfig.8
 @@ -270,6 +270,8 @@ scan for a valid Master Boot Record.
  scan for a valid GUID partition table.
  .It ffs
  scan for a valid FFS file system.
 +.It zfs
 +scan for a valid ZFS vdev label (if compiled with MKZFS).
  .It re-enter
  prompt for passphrase twice, and ensure entered passphrases are
  identical.
 diff --git a/sbin/cgdconfig/cgdconfig.c b/sbin/cgdconfig/cgdconfig.c
 index 9a634ef37e2..fe2c394b821 100644
 --- a/sbin/cgdconfig/cgdconfig.c
 +++ b/sbin/cgdconfig/cgdconfig.c
 @@ -73,6 +73,11 @@ __RCSID("$NetBSD: cgdconfig.c,v 1.61 2022/11/17
 06:40:38 chs Exp $");

  #include <ufs/ffs/fs.h>

 +#ifdef HAVE_ZFS
 +#include <sys/vdev_impl.h>
 +#include <sha2.h>
 +#endif
 +
  #include "params.h"
  #include "pkcs5_pbkdf2.h"
  #include "utils.h"
 @@ -170,6 +175,9 @@ static int	 verify_ffs(int);
  static int	 verify_reenter(struct params *);
  static int	 verify_mbr(int);
  static int	 verify_gpt(int);
 +#ifdef HAVE_ZFS
 +static int	 verify_zfs(int);
 +#endif

  __dead static void	 usage(void);

 @@ -1024,6 +1032,10 @@ verify(struct params *p, int fd)
  		return verify_mbr(fd);
  	case VERIFY_GPT:
  		return verify_gpt(fd);
 +#ifdef HAVE_ZFS
 +	case VERIFY_ZFS:
 +		return verify_zfs(fd);
 +#endif
  	default:
  		warnx("unimplemented verification method");
  		return -1;
 @@ -1182,6 +1194,103 @@ verify_gpt(int fd)
  	return ret;
  }

 +#ifdef HAVE_ZFS
 +
 +#define ZIO_CHECKSUM_BE(zcp)					\
 +{								\
 +	(zcp)->zc_word[0] = BE_64((zcp)->zc_word[0]);		\
 +	(zcp)->zc_word[1] = BE_64((zcp)->zc_word[1]);		\
 +	(zcp)->zc_word[2] = BE_64((zcp)->zc_word[2]);		\
 +	(zcp)->zc_word[3] = BE_64((zcp)->zc_word[3]);		\
 +}
 +
 +static int
 +verify_zfs(int fd)
 +{
 +	vdev_phys_t *vdev_phys;
 +	off_t vdev_size, vdev_phys_off;
 +	ssize_t ret;
 +	bool byteswap;
 +	zio_cksum_t cksum_found, cksum_real;
 +	SHA256_CTX ctx;
 +
 +	if (prog_ioctl(fd, DIOCGMEDIASIZE, &vdev_size) == -1) {
 +		warn("ioctl");
 +		return 1;
 +	}
 +
 +	vdev_phys = emalloc(sizeof *vdev_phys);
 +	for (int l = 0; l < VDEV_LABELS; l++) {
 +		vdev_phys_off = ( l < VDEV_LABELS/2 ?
 +		    l * sizeof(vdev_label_t) :
 +		    vdev_size - (VDEV_LABELS-l) * sizeof(vdev_label_t) ) \
 +		    + offsetof(vdev_label_t, vl_vdev_phys);
 +
 +		ret = prog_pread(fd, vdev_phys, sizeof *vdev_phys,
 +		    vdev_phys_off);
 +		if (ret < 0) {
 +			warn("pread");
 +			ret = 1;
 +			break;
 +		} else if ((size_t)ret < sizeof *vdev_phys) {
 +			warnx("pread: incomplete block");
 +			ret = 1;
 +			break;
 +		}
 +
 +		ret = 0;
 +
 +		if (vdev_phys->vp_zbt.zec_magic == BSWAP_64(ZEC_MAGIC))
 +			byteswap = true;
 +		else if (vdev_phys->vp_zbt.zec_magic == ZEC_MAGIC)
 +			byteswap = false;
 +		else {
 +			ret = 1;
 +			break;
 +		};
 +
 +		cksum_found = vdev_phys->vp_zbt.zec_cksum;
 +		if (byteswap)
 +			ZIO_CHECKSUM_BSWAP(&cksum_found);
 +
 +		ZIO_SET_CHECKSUM(&vdev_phys->vp_zbt.zec_cksum,
 +		    vdev_phys_off, 0, 0, 0);
 +		if (byteswap)
 +			ZIO_CHECKSUM_BSWAP(&vdev_phys->vp_zbt.zec_cksum);
 +
 +		SHA256Init(&ctx);
 +		SHA256Update(&ctx, (uint8_t *)vdev_phys, sizeof *vdev_phys);
 +		SHA256Final(&cksum_real, &ctx);
 +
 +		/*
 +		 * For historical reasons the on-disk sha256 checksums are
 +		 * always in big endian format.
 +		 * (see cddl/osnet/dist/uts/common/fs/zfs/sha256.c)
 +		 */
 +		ZIO_CHECKSUM_BE(&cksum_real);
 +
 +		if (!ZIO_CHECKSUM_EQUAL(cksum_found, cksum_real)) {
 +			warnx("checksum mismatch on vdev label %d", l);
 +			warnx("found %lx, %lx, %lx, %lx",
 +			    (unsigned long)cksum_found.zc_word[0],
 +			    (unsigned long)cksum_found.zc_word[1],
 +			    (unsigned long)cksum_found.zc_word[2],
 +			    (unsigned long)cksum_found.zc_word[3]);
 +			warnx("expected %lx, %lx, %lx, %lx",
 +			    (unsigned long)cksum_real.zc_word[0],
 +			    (unsigned long)cksum_real.zc_word[1],
 +			    (unsigned long)cksum_real.zc_word[2],
 +			    (unsigned long)cksum_real.zc_word[3]);
 +			ret = 1;
 +			break;
 +		}
 +	}
 +	free(vdev_phys);
 +	return ret;
 +}
 +
 +#endif
 +
  static off_t sblock_try[] = SBLOCKSEARCH;

  static int
 diff --git a/sbin/cgdconfig/params.c b/sbin/cgdconfig/params.c
 index 91af02c21cc..5da25ec0595 100644
 --- a/sbin/cgdconfig/params.c
 +++ b/sbin/cgdconfig/params.c
 @@ -287,6 +287,10 @@ params_verify_method(string_t *in)
  		p->verify_method = VERIFY_MBR;
  	if (!strcmp("gpt", vm))
  		p->verify_method = VERIFY_GPT;
 +#ifdef HAVE_ZFS
 +	if (!strcmp("zfs", vm))
 +		p->verify_method = VERIFY_ZFS;
 +#endif

  	string_free(in);

 @@ -1065,6 +1069,11 @@ params_fput(struct params *p, FILE *f)
  	case VERIFY_GPT:
  		print_kvpair_cstr(f, ts, "verify_method", "gpt");
  		break;
 +#ifdef HAVE_ZFS
 +	case VERIFY_ZFS:
 +		print_kvpair_cstr(f, ts, "verify_method", "zfs");
 +		break;
 +#endif
  	default:
  		warnx("unsupported verify_method (%d)", p->verify_method);
  		return -1;
 diff --git a/sbin/cgdconfig/params.h b/sbin/cgdconfig/params.h
 index fb79a0deb80..ef49dde6aba 100644
 --- a/sbin/cgdconfig/params.h
 +++ b/sbin/cgdconfig/params.h
 @@ -81,6 +81,7 @@ struct params {
  #define VERIFY_REENTER		0x4
  #define VERIFY_MBR      	0x5
  #define VERIFY_GPT      	0x6
 +#define VERIFY_ZFS      	0x7

  /* shared key derivation methods */

 -- 
 2.44.0

 -- 
 Malte Dehling

From: "Christos Zoulas" <christos@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/58212 CVS commit: src/sbin/cgdconfig
Date: Sun, 12 May 2024 14:02:16 -0400

 Module Name:	src
 Committed By:	christos
 Date:		Sun May 12 18:02:16 UTC 2024

 Modified Files:
 	src/sbin/cgdconfig: Makefile cgdconfig.8 cgdconfig.c params.c params.h

 Log Message:
 PR/58212: Malte Dehling: Add zfs verification method


 To generate a diff of this commit:
 cvs rdiff -u -r1.22 -r1.23 src/sbin/cgdconfig/Makefile
 cvs rdiff -u -r1.57 -r1.58 src/sbin/cgdconfig/cgdconfig.8
 cvs rdiff -u -r1.62 -r1.63 src/sbin/cgdconfig/cgdconfig.c
 cvs rdiff -u -r1.34 -r1.35 src/sbin/cgdconfig/params.c
 cvs rdiff -u -r1.14 -r1.15 src/sbin/cgdconfig/params.h

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.47 2022/09/11 19:34:41 kim Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2024 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.