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
[email protected]


Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to