NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: bin/58212: cgdconfig(8): Add zfs verification method



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.)


Home | Main Index | Thread Index | Old Index