Attempt to add multiple device attachment to "geli attach"
Karl Denninger
karl at denninger.net
Sun Sep 28 14:24:11 UTC 2014
Yes, I wrote a script to handle it in the meantime as well. The way the
geli command is architected it is rather difficult to do properly in the
code itself....
On 9/28/2014 8:15 AM, Pawel Jakub Dawidek wrote:
> 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 at denninger.net
>>
>>
>
>
--
Karl Denninger
karl at denninger.net <mailto:karl at denninger.net>
/The Market Ticker/
-------------- 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/20140928/e4ee391f/attachment.bin>
More information about the freebsd-geom
mailing list