Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 28 Sep 2014 15:15:16 +0200
From:      Pawel Jakub Dawidek <pjd@FreeBSD.org>
To:        Karl Denninger <karl@denninger.net>
Cc:        freebsd-geom@freebsd.org
Subject:   Re: Attempt to add multiple device attachment to "geli attach"
Message-ID:  <20140928131516.GE1620@garage.freebsd.pl>
In-Reply-To: <54076CFE.5010308@denninger.net>
References:  <54076871.5010405@denninger.net> <54076CFE.5010308@denninger.net>

next in thread | previous in thread | raw e-mail | index | archive | help
Hi Karl,

I like the idea. I myself currently use a script which obtains the
passphrase:

	echo -n "Enter passphrase: "
	stty -echo
	read pass
	stty echo
	echo

And then iterates over all providers I want to attach and uses -j option
to provide the key for the attach subcommand.

To make it integral part of geli and to be complete we would need to add
support for multiple providers to the following subcommands: init,
attach, onetime, setkey, delkey and resume.

On Wed, Sep 03, 2014 at 02:33:18PM -0500, Karl Denninger wrote:
> 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@denninger.net
> 
> 



-- 
Pawel Jakub Dawidek                       http://www.wheelsystems.com
FreeBSD committer                         http://www.FreeBSD.org
Am I Evil? Yes, I Am!                     http://mobter.com



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20140928131516.GE1620>