Attempt to add multiple device attachment to "geli attach"

Karl Denninger karl at denninger.net
Wed Sep 3 19:33:25 UTC 2014


Never mind... I know what I missed -- the key generation that is passed 
in is dependent on the metadata read from the userspace.

More work to do here.... will have to pass a separate key structure for 
each disk and it will also require some more work in the userspace 
command area so it doesn't prompt a second time for a password.

I'll post the completed patch set once I have it if people here think it 
would be interesting.

On 9/3/2014 14:13, Karl Denninger wrote:
> I'm aware of the potential issues here in terms of keying risks, but 
> there are plenty of reasons to support this capability with the 
> largest one being ZFS volumes that you wish to run encrypted.
>
> Take the following:
>
> label/pool0
> label/pool1
> label/pool2
> label/pool3
>
> (all relative to /dev, of course)
>
> These are all gpt partitions on different devices (typically full 
> disks less labels.)  You "geli init" them and then attach them and 
> build a raidz2 pool on that.
>
> OK, now the system is rebooted.  If you use the rc.conf file's option 
> to request geli passwords during the boot you had better not screw up 
> three times for only ONE of these volumes or the pool WILL come up 
> degraded!  Needless to say that's not nice.  It's even worse if it's a 
> raidz pool, you blow it, you reattach that disk and allow it to 
> resilver *and take a disk error on the remaining drives during the 
> resilver* -- now you're completely hosed.
>
> So, here's the idea -- if you use the same password and/or keyfile for 
> ALL of the volumes then either they ALL mount (if you get it right) or 
> NONE of them mount (if you get it wrong.)  Now the pool won't import 
> if you get it wrong and you're safe from the risk of taking a forced 
> resilver and potential data loss.
>
> The geom subclass command has a simple "nargs" test (must be "1") in 
> the attach command; I replaced that with "nargs < 1" for the error 
> case.  Now I can pass multiple devices to the kernel's geom handler 
> and they get passed to the kernel ctl handler.
>
> The following patch should, I believe, work -- but it doesn't. The 
> first disk attaches but the second one that was init'd with the same 
> passphrase fails.
>
> As near as I can tell the key components are not picked up off the 
> metadata until the geom driver gets ahold of it -- and thus the second 
> decryption attempt should work since on the second iteration through 
> the code grabs the key parameters off the request a second time.
>
> But I'm obviously missing something because the second volume returns 
> "Wrong key for ...."
>
> Ideas?
>
> Patch is relative to /usr/src/sys/geom/eli:
>
> *** g_eli_ctl.c.orig    Wed Sep  3 13:11:52 2014
> --- g_eli_ctl.c    Wed Sep  3 13:19:15 2014
> ***************
> *** 60,65 ****
> --- 60,68 ----
>       int *nargs, *detach, *readonly;
>       int keysize, error;
>       u_int nkey;
> +     char param[16];
> +
> +     u_int count;
>
>       g_topology_assert();
>
> ***************
> *** 68,74 ****
>           gctl_error(req, "No '%s' argument.", "nargs");
>           return;
>       }
> !     if (*nargs != 1) {
>           gctl_error(req, "Invalid number of arguments.");
>           return;
>       }
> --- 71,77 ----
>           gctl_error(req, "No '%s' argument.", "nargs");
>           return;
>       }
> !     if (*nargs < 1) {
>           gctl_error(req, "Invalid number of arguments.");
>           return;
>       }
> ***************
> *** 84,147 ****
>           gctl_error(req, "No '%s' argument.", "readonly");
>           return;
>       }
>
> !     name = gctl_get_asciiparam(req, "arg0");
> !     if (name == NULL) {
> !         gctl_error(req, "No 'arg%u' argument.", 0);
> !         return;
> !     }
> !     if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
> !         name += strlen("/dev/");
> !     pp = g_provider_by_name(name);
> !     if (pp == NULL) {
> !         gctl_error(req, "Provider %s is invalid.", name);
> !         return;
> !     }
> !     error = g_eli_read_metadata(mp, pp, &md);
> !     if (error != 0) {
> !         gctl_error(req, "Cannot read metadata from %s (error=%d).",
> !             name, error);
> !         return;
> !     }
> !     if (md.md_keys == 0x00) {
> !         bzero(&md, sizeof(md));
> !         gctl_error(req, "No valid keys on %s.", pp->name);
> !         return;
> !     }
> !
> !     key = gctl_get_param(req, "key", &keysize);
> !     if (key == NULL || keysize != G_ELI_USERKEYLEN) {
> !         bzero(&md, sizeof(md));
> !         gctl_error(req, "No '%s' argument.", "key");
> !         return;
> !     }
> !
> !     error = g_eli_mkey_decrypt(&md, key, mkey, &nkey);
> !     bzero(key, keysize);
> !     if (error == -1) {
> !         bzero(&md, sizeof(md));
> !         gctl_error(req, "Wrong key for %s.", pp->name);
> !         return;
> !     } else if (error > 0) {
> !         bzero(&md, sizeof(md));
> !         gctl_error(req, "Cannot decrypt Master Key for %s (error=%d).",
> !             pp->name, error);
> !         return;
> !     }
> !     G_ELI_DEBUG(1, "Using Master Key %u for %s.", nkey, pp->name);
> !
> !     if (*detach && *readonly) {
>           bzero(&md, sizeof(md));
> -         gctl_error(req, "Options -d and -r are mutually exclusive.");
> -         return;
>       }
> -     if (*detach)
> -         md.md_flags |= G_ELI_FLAG_WO_DETACH;
> -     if (*readonly)
> -         md.md_flags |= G_ELI_FLAG_RO;
> -     g_eli_create(req, mp, pp, &md, mkey, nkey);
> -     bzero(mkey, sizeof(mkey));
> -     bzero(&md, sizeof(md));
>   }
>
>   static struct g_eli_softc *
> --- 87,152 ----
>           gctl_error(req, "No '%s' argument.", "readonly");
>           return;
>       }
> +     for (count = 0; count < *nargs; count++) {
> +         snprintf(param, sizeof(param), "arg%d", count);
> +         name = gctl_get_asciiparam(req, param);
> +         if (name == NULL) {
> +             gctl_error(req, "No 'arg%u' argument.", count);
> +             return;
> +         }
> +         if (strncmp(name, "/dev/", strlen("/dev/")) == 0)
> +             name += strlen("/dev/");
> +         pp = g_provider_by_name(name);
> +         if (pp == NULL) {
> +             gctl_error(req, "Provider %s is invalid.", name);
> +             return;
> +         }
> +         error = g_eli_read_metadata(mp, pp, &md);
> +         if (error != 0) {
> +             gctl_error(req, "Cannot read metadata from %s (error=%d).",
> +                 name, error);
> +             return;
> +         }
> +         if (md.md_keys == 0x00) {
> +             bzero(&md, sizeof(md));
> +             gctl_error(req, "No valid keys on %s.", pp->name);
> +             return;
> +         }
> +
> +         key = gctl_get_param(req, "key", &keysize);
> +         if (key == NULL || keysize != G_ELI_USERKEYLEN) {
> +             bzero(&md, sizeof(md));
> +             gctl_error(req, "No '%s' argument.", "key");
> +             return;
> +         }
>
> !         error = g_eli_mkey_decrypt(&md, key, mkey, &nkey);
> !         bzero(key, keysize);
> !         if (error == -1) {
> !             bzero(&md, sizeof(md));
> !             gctl_error(req, "Wrong key for %s.", pp->name);
> !             return;
> !         } else if (error > 0) {
> !             bzero(&md, sizeof(md));
> !             gctl_error(req, "Cannot decrypt Master Key for %s 
> (error=%d).",
> !                 pp->name, error);
> !             return;
> !         }
> !         G_ELI_DEBUG(1, "Using Master Key %u for %s.", nkey, pp->name);
> !
> !         if (*detach && *readonly) {
> !             bzero(&md, sizeof(md));
> !             gctl_error(req, "Options -d and -r are mutually 
> exclusive.");
> !             return;
> !         }
> !         if (*detach)
> !             md.md_flags |= G_ELI_FLAG_WO_DETACH;
> !         if (*readonly)
> !             md.md_flags |= G_ELI_FLAG_RO;
> !         g_eli_create(req, mp, pp, &md, mkey, nkey);
> !         bzero(mkey, sizeof(mkey));
>           bzero(&md, sizeof(md));
>       }
>   }
>
>   static struct g_eli_softc *
>
>
> ------------------------
>

-- 
-- Karl
karl at denninger.net


-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 2711 bytes
Desc: S/MIME Cryptographic Signature
URL: <http://lists.freebsd.org/pipermail/freebsd-geom/attachments/20140903/819f34f8/attachment.bin>


More information about the freebsd-geom mailing list