Module Name: src Committed By: martin Date: Fri Oct 11 08:54:39 UTC 2024
Modified Files: src/sbin/cgdconfig [netbsd-10]: Makefile cgdconfig.8 cgdconfig.c params.c params.h Log Message: Pull up following revision(s) (requested by riastradh in ticket #941): sbin/cgdconfig/params.c: revision 1.35 sbin/cgdconfig/params.h: revision 1.15 sbin/cgdconfig/cgdconfig.c: revision 1.62 sbin/cgdconfig/cgdconfig.c: revision 1.63 sbin/cgdconfig/cgdconfig.8: revision 1.58 sbin/cgdconfig/cgdconfig.8: revision 1.59 sbin/cgdconfig/Makefile: revision 1.23 cgdconfig(8): KNF in cgdconfig.c. No functional change intended. PR/58212: Malte Dehling: Add zfs verification method cgdconfig(8): Estimate verify methods' false accept probabilities. An addendum following discussion around: PR bin/58212: cgdconfig(8): Add zfs verification method To generate a diff of this commit: cvs rdiff -u -r1.22 -r1.22.2.1 src/sbin/cgdconfig/Makefile cvs rdiff -u -r1.57 -r1.57.2.1 src/sbin/cgdconfig/cgdconfig.8 cvs rdiff -u -r1.61 -r1.61.2.1 src/sbin/cgdconfig/cgdconfig.c cvs rdiff -u -r1.34 -r1.34.2.1 src/sbin/cgdconfig/params.c cvs rdiff -u -r1.14 -r1.14.2.1 src/sbin/cgdconfig/params.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sbin/cgdconfig/Makefile diff -u src/sbin/cgdconfig/Makefile:1.22 src/sbin/cgdconfig/Makefile:1.22.2.1 --- src/sbin/cgdconfig/Makefile:1.22 Fri Aug 12 10:49:17 2022 +++ src/sbin/cgdconfig/Makefile Fri Oct 11 08:54:39 2024 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.22 2022/08/12 10:49:17 riastradh Exp $ +# $NetBSD: Makefile,v 1.22.2.1 2024/10/11 08:54:39 martin Exp $ RUMPPRG=cgdconfig MAN= cgdconfig.8 @@ -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> Index: src/sbin/cgdconfig/cgdconfig.8 diff -u src/sbin/cgdconfig/cgdconfig.8:1.57 src/sbin/cgdconfig/cgdconfig.8:1.57.2.1 --- src/sbin/cgdconfig/cgdconfig.8:1.57 Fri Aug 12 10:49:47 2022 +++ src/sbin/cgdconfig/cgdconfig.8 Fri Oct 11 08:54:39 2024 @@ -1,4 +1,4 @@ -.\" $NetBSD: cgdconfig.8,v 1.57 2022/08/12 10:49:47 riastradh Exp $ +.\" $NetBSD: cgdconfig.8,v 1.57.2.1 2024/10/11 08:54:39 martin Exp $ .\" .\" Copyright (c) 2002, The NetBSD Foundation, Inc. .\" All rights reserved. @@ -27,7 +27,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd November 4, 2021 +.Dd May 12, 2024 .Dt CGDCONFIG 8 .Os .Sh NAME @@ -270,12 +270,115 @@ 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. This method only works with the argon2id, pkcs5_pbkdf2/sha1, and pkcs5_pbkdf2 key generators. .El +.Pp +If a wrong key is generated, e.g. if the passphrase is entered +incorrectly, the disk content will appear to be randomized. +Assuming uniform random disk content, each verification method has some +probability of falsely accepting a wrong key anyway. +The probability for each method is as follows: +.Bl -column "disklabel" "matching \*(Ge160-bit hashes" "1 - (1 - 1/2^80)^1946 < 1/6e20" +.It Sy method Ta Sy verifies Ta Sy "P(false accept)" +.It Li none Ta No nothing Ta "1" Ta +.\" disklabel: +.\" - scans SCANSIZE=8192 bytes with disklabel_scan, which... +.\" - checks {0, 4, 8, 12, ..., SCANSIZE=8192 - sizeof(struct +.\" disklabel)=408}, 1946 options total, for a matching 64-bit +.\" quantity (d_magic=DISKMAGIC, d_magic2=DISKMAGIC) plus a matching +.\" 16-bit cksum (plus a plausible d_npartitions but let's ignore +.\" that) +.\" Pr[false accept] = Pr[exists i. scan[i] matches 80-bit magic/cksum] +.\" = 1 - Pr[not exists i. scan[i] does not match 80-bit magic/cksum] +.\" = 1 - Pr[forall i. scan[i] does not match 80-bit magic/cksum] +.\" = 1 - \prod_i Pr[scan[i] does not match 80-bit magic/cksum] +.\" = 1 - \prod_i (1 - Pr[scan[i] matches 80-bit magic/cksum]) +.\" = 1 - \prod_i (1 - 1/2^80) +.\" = 1 - (1 - 1/2^80)^1946 +.\" = 1 - exp(1946*log(1 - 1/2^80)) +.\" = -expm1(1946*log1p(-1/2^80)) +.\" <= 1/621e18 <= 1/6e20 (one in six hundred quintillion) +.It Li disklabel Ta No 64-bit magic strings w/16-bit cksum in any of 1946 places Ta "1 - (1 - 1/2^80)^1946 < 1/6e20" +.\" mbr: +.\" - checks exactly one location +.\" - checks for exactly one magic 16-bit constant +.\" Pr[false accept] = 1/2^16 = 1/65536 +.It Li mbr Ta No 16-bit magic string Ta "1/65536" +.\" gpt: +.\" - scans SCANSIZE=8192 bytes +.\" - checks blksizes DEV_BSIZE=512, 1024, 2048, 4096 +.\" - checks for 64-bit sig, 32-bit rev, 32-bit size, 32-bit cksum +.\" Pr[false accept] +.\" = Pr[exists blksz. hdr[blksz] matches 160-bit magic/cksum] +.\" = 1 - Pr[not exists blksz. hdr[blksz] matches 160-bit magic/cksum] +.\" = 1 - Pr[forall blksz. hdr[blksz] does not match 160-bit magic/cksum] +.\" = 1 - \prod_blksz Pr[hdr[blksz] does not match 160-bit magic/cksum] +.\" = 1 - \prod_blksz (1 - Pr[hdr[blksz] matches 160-bit magic/cksum]) +.\" = 1 - \prod_blksz (1 - 1/2^160) +.\" = 1 - (1 - 1/2^160)^4 +.\" = 1 - exp(4*log(1 - 1/2^160)) +.\" = -expm1(4*log1p(-1/2^160)) +.\" <= 1/3e47 +.It Li gpt Ta No 128-bit magic string w/32-bit cksum in any of 4 places Ta "1 - (1 - 1/2^160)^4 < 1/3e47" Ta +.\" ffs: +.\" - checks four locations in SBLOCKSEARCH (sblock_try) +.\" - checks for any of six magic 32-bit constants +.\" Pr[false accept] = Pr[exists i. sblock_try[i] in {magic[0], ..., magic[5]}] +.\" = 1 - Pr[not exists i. sblock_try[i] in {magic[0], ..., magic[5]}] +.\" = 1 - Pr[forall i. sblock_try[i] not in {magic[0], ..., magic[5]}] +.\" = 1 - \prod_i Pr[sblock_try[i] not in {magic[0], ..., magic[5]}] +.\" = 1 - \prod_i (1 - Pr[sblock_try[i] in {magic[0], ..., magic[5]}]) +.\" = 1 - \prod_i (1 - 6/2^32) +.\" = 1 - (1 - 6/2^32)^4 +.\" = 1 - exp(4*log(1 - 6/2^32)) +.\" = -expm1(4*log1p(-6/2^32)) +.\" <= 1/178e6 <= 1/1e8 (one in a hundred million) +.It Li ffs Ta No any of 6 32-bit magic strings in any of 4 places Ta "1 - (1 - 6/2^32)^4 < 1/1e8" Ta +.\" zfs: +.\" - checks four locations (VDEV_LABELS) +.\" - checks for any of two magic 64-bit constants (ZEC_MAGIC or bswap) +.\" - checks for 256-bit SHA256 hash +.\" Pr[false accept] = Pr[exists i. label[i] matches 320-bit magic/cksum] +.\" = 1 - Pr[not exists i. label[i] matches 320-bit magic/cksum] +.\" = 1 - Pr[forall i. label[i] does not match 320-bit magic/cksum] +.\" = 1 - \prod_i Pr[label[i] does not match 320-bit magic/cksum] +.\" = 1 - \prod_i (1 - Pr[label[i] does matches 320-bit magic/cksum]) +.\" = 1 - \prod_i (1 - 2/2^230) +.\" = 1 - (1 - 2/2^230)^4 +.\" = -expm1(4*log1p(-2/2^230)) +.\" <= 1/2e68 +.It Li zfs Ta No any of 2 64-bit magic strings w/256-bit cksum in any of 4 places Ta "1 - (1 - 2/2^320)^4 < 1/1e68" +.\" re-enter: +.\" - checks whether >=160-bit hash matches +.\" Pr[false accept] = Pr[H(pw1) = H(pw2) | pw1 != pw2] <= 1/2^160 < 1/1e48 +.It Li re-enter Ta No matching \*(Ge160-bit hashes Ta "\*(Le 1/2^160 < 1/1e48" +.El +.Pp +Note that aside from the +.Ql none +method, which accepts any key unconditionally, the +.Ql mbr +method and to a lesser extent the +.Ql ffs +method also accept a wrong key with a much higher probability than +cryptography normally deals in. +.Pp +This is not a security vulnerability in the confidentiality of +.Xr cgd 4 +against an adversary, but it may be alarming for a user if a disk is +configured with a mistyped passphrase, +.Nm +.Em accepts the wrong key , +and the content appears to be garbage \(em for example, fsck may fail +with scary warnings, and any writes to the disk configured with the +wrong key will corrupt the original plaintext content under the right +key. .Ss /etc/cgd/cgd.conf The file .Pa /etc/cgd/cgd.conf Index: src/sbin/cgdconfig/cgdconfig.c diff -u src/sbin/cgdconfig/cgdconfig.c:1.61 src/sbin/cgdconfig/cgdconfig.c:1.61.2.1 --- src/sbin/cgdconfig/cgdconfig.c:1.61 Thu Nov 17 06:40:38 2022 +++ src/sbin/cgdconfig/cgdconfig.c Fri Oct 11 08:54:39 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: cgdconfig.c,v 1.61 2022/11/17 06:40:38 chs Exp $ */ +/* $NetBSD: cgdconfig.c,v 1.61.2.1 2024/10/11 08:54:39 martin Exp $ */ /*- * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc. @@ -33,7 +33,7 @@ #ifndef lint __COPYRIGHT("@(#) Copyright (c) 2002, 2003\ The NetBSD Foundation, Inc. All rights reserved."); -__RCSID("$NetBSD: cgdconfig.c,v 1.61 2022/11/17 06:40:38 chs Exp $"); +__RCSID("$NetBSD: cgdconfig.c,v 1.61.2.1 2024/10/11 08:54:39 martin Exp $"); #endif #ifdef HAVE_ARGON2 @@ -73,6 +73,11 @@ __RCSID("$NetBSD: cgdconfig.c,v 1.61 202 #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" @@ -98,11 +103,11 @@ enum action { /* if nflag is set, do not configure/unconfigure the cgd's */ -int nflag = 0; +static int nflag = 0; /* if Sflag is set, generate shared keys */ -int Sflag = 0; +static int Sflag = 0; /* if pflag is set to PFLAG_STDIN read from stdin rather than getpass(3) */ @@ -110,7 +115,7 @@ int Sflag = 0; #define PFLAG_GETPASS_ECHO 0x02 #define PFLAG_GETPASS_MASK 0x03 #define PFLAG_STDIN 0x04 -int pflag = PFLAG_GETPASS; +static int pflag = PFLAG_GETPASS; /* * When configuring all cgds, save a cache of shared keys for key @@ -127,7 +132,7 @@ struct sharedkey { SLIST_ENTRY(sharedkey) used; int verified; }; -LIST_HEAD(, sharedkey) sharedkeys; +static LIST_HEAD(, sharedkey) sharedkeys; SLIST_HEAD(sharedkeyhits, sharedkey); static int configure(int, char **, struct params *, int); @@ -170,11 +175,14 @@ 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); /* Verbose Framework */ -unsigned verbose = 0; +static unsigned verbose = 0; #define VERBOSE(x,y) if (verbose >= x) y #define VPRINTF(x,y) if (verbose >= x) (void)printf y @@ -636,12 +644,12 @@ getkey_argon2id(const char *target, stru char *passp; char buf[1024]; uint8_t raw[256]; - int err; + int error; snprintf(buf, sizeof(buf), "%s's passphrase%s:", target, pflag & PFLAG_GETPASS_ECHO ? " (echo)" : ""); passp = maybe_getpass(buf); - if ((err = argon2_hash(kg->kg_iterations, kg->kg_memory, + if ((error = argon2_hash(kg->kg_iterations, kg->kg_memory, kg->kg_parallelism, passp, strlen(passp), bits_getbuf(kg->kg_salt), @@ -649,7 +657,7 @@ getkey_argon2id(const char *target, stru raw, sizeof(raw), NULL, 0, Argon2_id, kg->kg_version)) != ARGON2_OK) { - warnx("failed to generate Argon2id key, error code %d", err); + warnx("failed to generate Argon2id key, error code %d", error); return NULL; } @@ -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; @@ -1161,7 +1173,6 @@ verify_gpt(int fd) return -1; } - ret = 1; for (blksize = DEV_BSIZE; (off = (blksize * GPT_HDR_BLKNO)) <= SCANSIZE - sizeof(hdr); blksize <<= 1) { @@ -1173,15 +1184,114 @@ verify_gpt(int fd) hdr.hdr_crc_self = 0; if (crc32(&hdr, sizeof(hdr))) { - ret = 0; - break; + return 0; } } } - return ret; + return 1; +} + +#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) +{ + off_t vdev_size; + int rv = 1; + + if (prog_ioctl(fd, DIOCGMEDIASIZE, &vdev_size) == -1) { + warn("%s: ioctl", __func__); + return rv; + } + + vdev_phys_t *vdev_phys = emalloc(sizeof(*vdev_phys)); + for (size_t i = 0; i < VDEV_LABELS; i++) { + off_t vdev_phys_off = (i < VDEV_LABELS / 2 ? + i * sizeof(vdev_label_t) : + vdev_size - (VDEV_LABELS - i) * sizeof(vdev_label_t)) + + offsetof(vdev_label_t, vl_vdev_phys); + + ssize_t ret = prog_pread(fd, vdev_phys, sizeof(*vdev_phys), + vdev_phys_off); + if (ret == -1) { + warn("%s: read failed", __func__); + goto out; + } + if ((size_t)ret < sizeof(*vdev_phys)) { + warnx("%s: incomplete block", __func__); + goto out; + } + + bool byteswap; + switch (vdev_phys->vp_zbt.zec_magic) { + case BSWAP_64(ZEC_MAGIC): + byteswap = true; + break; + case ZEC_MAGIC: + byteswap = false; + break; + default: + goto out; + } + + zio_cksum_t 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); + } + + SHA256_CTX ctx; + zio_cksum_t cksum_real; + + 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("%s: checksum mismatch on vdev label %zu", + __func__, i); + warnx("%s: found %#jx, %#jx, %#jx, %#jx", __func__, + (uintmax_t)cksum_found.zc_word[0], + (uintmax_t)cksum_found.zc_word[1], + (uintmax_t)cksum_found.zc_word[2], + (uintmax_t)cksum_found.zc_word[3]); + warnx("%s: expected %#jx, %#jx, %#jx, %#jx", __func__, + (uintmax_t)cksum_real.zc_word[0], + (uintmax_t)cksum_real.zc_word[1], + (uintmax_t)cksum_real.zc_word[2], + (uintmax_t)cksum_real.zc_word[3]); + goto out; + } + } + rv = 0; +out: + free(vdev_phys); + return rv; } +#endif + static off_t sblock_try[] = SBLOCKSEARCH; static int @@ -1507,7 +1617,8 @@ iv_method(int mode) static void -show(const char *dev) { +show(const char *dev) +{ char path[64]; struct cgd_user cgu; int fd; @@ -1532,11 +1643,13 @@ show(const char *dev) { } dev = devname(cgu.cgu_dev, S_IFBLK); - if (dev != NULL) + if (dev != NULL) { printf("%s ", dev); - else - printf("dev %llu,%llu ", (unsigned long long)major(cgu.cgu_dev), + } else { + printf("dev %llu,%llu ", + (unsigned long long)major(cgu.cgu_dev), (unsigned long long)minor(cgu.cgu_dev)); + } if (verbose) printf("%s ", cgu.cgu_alg); Index: src/sbin/cgdconfig/params.c diff -u src/sbin/cgdconfig/params.c:1.34 src/sbin/cgdconfig/params.c:1.34.2.1 --- src/sbin/cgdconfig/params.c:1.34 Fri Aug 12 10:49:35 2022 +++ src/sbin/cgdconfig/params.c Fri Oct 11 08:54:39 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: params.c,v 1.34 2022/08/12 10:49:35 riastradh Exp $ */ +/* $NetBSD: params.c,v 1.34.2.1 2024/10/11 08:54:39 martin Exp $ */ /*- * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ #include <sys/cdefs.h> #ifndef lint -__RCSID("$NetBSD: params.c,v 1.34 2022/08/12 10:49:35 riastradh Exp $"); +__RCSID("$NetBSD: params.c,v 1.34.2.1 2024/10/11 08:54:39 martin Exp $"); #endif #include <sys/types.h> @@ -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; Index: src/sbin/cgdconfig/params.h diff -u src/sbin/cgdconfig/params.h:1.14 src/sbin/cgdconfig/params.h:1.14.2.1 --- src/sbin/cgdconfig/params.h:1.14 Fri Aug 12 10:49:35 2022 +++ src/sbin/cgdconfig/params.h Fri Oct 11 08:54:39 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: params.h,v 1.14 2022/08/12 10:49:35 riastradh Exp $ */ +/* $NetBSD: params.h,v 1.14.2.1 2024/10/11 08:54:39 martin Exp $ */ /*- * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc. @@ -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 */