Attempt to add multiple device attachment to "geli attach"

Karl Denninger karl at denninger.net
Wed Sep 3 19:15:35 UTC 2014


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/9a402fcc/attachment.bin>


More information about the freebsd-geom mailing list