discover: Recognise and open LUKS encrypted partitions
authorSamuel Mendoza-Jonas <sam@mendozajonas.com>
Thu, 14 Feb 2019 23:40:14 +0000 (10:40 +1100)
committerSamuel Mendoza-Jonas <sam@mendozajonas.com>
Tue, 26 Mar 2019 05:46:38 +0000 (16:46 +1100)
Handle devices encrypted with LUKS and call cryptsetup to open them if a
client sends the associated password.
If a new device has the "crypto_LUKS" filesystem type it is marked as a
LUKS device and sent to clients but further discovery is not performed.
Once a client sends the device's password cryptsetup is called to open
it. The opened device will appear separately, so the source device is
"forgotten" at this point and then the newly opened device is treated as
a normal partition. On destruction the device is "closed" with
cryptsetup so that discovery can start from the beginning.

Signed-off-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
discover/device-handler.c
discover/device-handler.h
discover/discover-server.c
discover/udev.c
test/parser/handler.c

index e75f41232db072e2db2a1aae4e6346b3574bdd99..d41bb4b0bb924e02b4d0b70f9021d77a08285242 100644 (file)
@@ -60,6 +60,13 @@ struct progress_info {
        struct list_item        list;
 };
 
+struct crypt_info {
+       struct discover_device  *source_device;
+       char                    *dm_name;
+
+       struct list_item        list;
+};
+
 struct device_handler {
        struct discover_server  *server;
        int                     dry_run;
@@ -95,6 +102,8 @@ struct device_handler {
        struct plugin_option    **plugins;
        unsigned int            n_plugins;
        bool                    plugin_installing;
+
+       struct list             crypt_devices;
 };
 
 static int mount_device(struct discover_device *dev);
@@ -265,9 +274,30 @@ void device_handler_destroy(struct device_handler *handler)
 static int destroy_device(void *arg)
 {
        struct discover_device *dev = arg;
+       struct process *p;
 
        umount_device(dev);
 
+       devmapper_destroy_snapshot(dev);
+
+       if (dev->crypt_device) {
+               const char *argv[] = {
+                       pb_system_apps.cryptsetup,
+                       "luksClose",
+                       dev->device->id,
+                       NULL
+               };
+
+               p = process_create(dev);
+               p->path = pb_system_apps.cryptsetup;
+               p->argv = argv;
+
+               if (process_run_async(p)) {
+                       pb_log("Failed to run cryptsetup\n");
+                       return -1;
+               }
+       }
+
        return 0;
 }
 
@@ -376,6 +406,7 @@ struct device_handler *device_handler_init(struct discover_server *server,
        list_init(&handler->unresolved_boot_options);
 
        list_init(&handler->progress);
+       list_init(&handler->crypt_devices);
 
        /* set up our mount point base */
        pb_mkdir_recursive(mount_base());
@@ -399,6 +430,7 @@ struct device_handler *device_handler_init(struct discover_server *server,
 void device_handler_reinit(struct device_handler *handler)
 {
        struct discover_boot_option *opt, *tmp;
+       struct crypt_info *crypt, *c;
        struct ramdisk_device *ramdisk;
        struct config *config;
        unsigned int i;
@@ -449,6 +481,11 @@ void device_handler_reinit(struct device_handler *handler)
 
        discover_server_notify_plugins_remove(handler->server);
 
+       /* forget encrypted devices */
+       list_for_each_entry_safe(&handler->crypt_devices, crypt, c, list)
+               talloc_free(crypt);
+       list_init(&handler->crypt_devices);
+
        set_env_variables(config_get());
 
        /* If the safe mode warning was active disable it now */
@@ -1230,6 +1267,116 @@ void device_handler_release_ramdisk(struct discover_device *device)
        device->ramdisk = NULL;
 }
 
+/*
+ * Check if a device name matches the name of an encrypted device that has been
+ * opened. If it matches remove it from the list and remove the original crypt
+ * discover device.
+ */
+bool device_handler_found_crypt_device(struct device_handler *handler,
+               const char *name)
+{
+       struct crypt_info *crypt, *c;
+
+       list_for_each_entry_safe(&handler->crypt_devices, crypt, c, list) {
+               if (!strncmp(crypt->dm_name, name, strlen(crypt->dm_name))) {
+                       device_handler_remove(handler, crypt->source_device);
+                       list_remove(&crypt->list);
+                       talloc_free(crypt);
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static void cryptsetup_cb(struct process *process)
+{
+       struct device_handler *handler = process->data;
+       struct crypt_info *crypt, *c;
+
+       if (process->exit_status == 0)
+               return;
+       device_handler_status_err(handler,
+                       _("Failed to open encrypted device %s"),
+                       process->argv[2]);
+
+       /*
+        * Failed to open the device; stop tracking it, but don't remove
+        * the source device.
+        */
+       list_for_each_entry_safe(&handler->crypt_devices, crypt, c, list) {
+               if (!strncmp(crypt->dm_name, process->argv[3],
+                                       strlen(crypt->dm_name))) {
+                       list_remove(&crypt->list);
+                       talloc_free(crypt);
+                       break;
+               }
+       }
+}
+
+void device_handler_open_encrypted_dev(struct device_handler *handler,
+               char *password, char *device_id)
+{
+       struct discover_device *dev;
+       struct crypt_info *crypt;
+       const char *device_path, **argv;
+       struct process *p;
+       char *name;
+       int result;
+
+       dev = device_lookup_by_id(handler, device_id);
+       if (!dev) {
+               pb_log_fn("Can't find device %s\n", device_id);
+               device_handler_status_err(handler,
+                               _("Encrypted device %s does not exist"),
+                               device_id);
+               return;
+       }
+
+       device_path = dev->device_path;
+       name = talloc_asprintf(handler, "luks_%s", device_id);
+
+       p = process_create(handler);
+       /* talloc argv under the process so we can access it in cryptsetup_cb */
+       argv = talloc_zero_array(p, const char *, 6);
+       argv[0] = talloc_strdup(argv, pb_system_apps.cryptsetup);
+       argv[1] = talloc_asprintf(argv, "luksOpen");
+       argv[2] = talloc_strdup(argv, device_path);
+       argv[3] = talloc_strdup(argv, name);
+       argv[4] = talloc_asprintf(argv, "-");
+       argv[5] = NULL;
+
+       p->path = pb_system_apps.cryptsetup;
+       p->argv = (const char **)argv;
+       p->exit_cb = cryptsetup_cb;
+       p->data = handler;
+       p->keep_stdout = true;
+       p->pipe_stdin = talloc_asprintf(p, "%s\n", password);
+
+       result = process_run_async(p);
+       if (result) {
+               pb_log("Failed to run cryptsetup\n");
+               return;
+       }
+
+       crypt = talloc(handler, struct crypt_info);
+       crypt->source_device = dev;
+       crypt->dm_name = name;
+       talloc_steal(crypt, name);
+       list_add(&handler->crypt_devices, &crypt->list);
+}
+
+void device_handler_add_encrypted_dev(struct device_handler *handler,
+               struct discover_device *dev)
+{
+       system_info_register_blockdev(dev->device->id, dev->uuid, "");
+       discover_server_notify_device_add(handler->server,
+                                         dev->device);
+       dev->notified = true;
+       if (!device_lookup_by_uuid(handler, dev->uuid))
+               device_handler_add_device(handler, dev);
+}
+
 /* Start discovery on a hotplugged device. The device will be in our devices
  * array, but has only just been initialised by the hotplug source.
  */
@@ -2121,7 +2268,6 @@ static int umount_device(struct discover_device *dev)
                return -1;
 
        dev->mounted = false;
-       devmapper_destroy_snapshot(dev);
 
        pb_rmdir_recursive(mount_base(), dev->mount_path);
 
index 9619a2df0c9df4ed5f078717fb3f35896fa388fa..65911208600fbd0a9563f1b988390478e3c25be0 100644 (file)
@@ -33,6 +33,7 @@ struct discover_device {
        bool                    mounted;
        bool                    mounted_rw;
        bool                    unmount;
+       bool                    crypt_device;
 
        bool                    notified;
 
@@ -89,6 +90,9 @@ const struct plugin_option *device_handler_get_plugin(
 struct network *device_handler_get_network(
                const struct device_handler *handler);
 
+bool device_handler_found_crypt_device(struct device_handler *handler,
+               const char *name);
+
 struct discover_device *discover_device_create(struct device_handler *handler,
                const char *uuid, const char *id);
 void device_handler_add_device(struct device_handler *handler,
@@ -98,6 +102,10 @@ void device_handler_add_ramdisk(struct device_handler *handler,
 struct ramdisk_device *device_handler_get_ramdisk(
                struct device_handler *handler);
 void device_handler_release_ramdisk(struct discover_device *device);
+void device_handler_open_encrypted_dev(struct device_handler *handler,
+               char *password, char *device_id);
+void device_handler_add_encrypted_dev(struct device_handler *handler,
+               struct discover_device *dev);
 int device_handler_discover(struct device_handler *handler,
                struct discover_device *dev);
 int device_handler_dhcp(struct device_handler *handler,
index 23d6113ec7ce5de5675feff0e761745d854c2442..1a332cbfbc76e8d5ef720158778308d066fd45bd 100644 (file)
@@ -365,13 +365,29 @@ static int discover_server_handle_auth_message(struct client *client,
                                        _("Password updated successfully"));
                }
                break;
+       case AUTH_MSG_DECRYPT:
+               if (!client->can_modify) {
+                       pb_log("Unauthenticated client tried to open encrypted device %s\n",
+                                       auth_msg->decrypt_dev.device_id);
+                       rc = -1;
+                       status->type = STATUS_ERROR;
+                       status->message = talloc_asprintf(status,
+                                       _("Must authenticate before opening encrypted device"));
+                       break;
+               }
+
+               device_handler_open_encrypted_dev(client->server->device_handler,
+                               auth_msg->decrypt_dev.password,
+                               auth_msg->decrypt_dev.device_id);
+               break;
        default:
                pb_log("%s: unknown op\n", __func__);
                rc = -1;
                break;
        }
 
-       write_boot_status_message(client->server, client, status);
+       if (status->message)
+               write_boot_status_message(client->server, client, status);
        talloc_free(status);
 
        return rc;
index fa5d4b417a0f7f2164b6f4245bcf20e09f52ab2e..0c3da66a6eeaa5347ae7ca137e57bb6779c714e8 100644 (file)
@@ -106,7 +106,7 @@ static int udev_handle_block_add(struct pb_udev *udev, struct udev_device *dev,
                "swap",
                NULL,
        };
-       bool cdrom, usb;
+       bool cdrom, usb, luks = false;
 
        typestr = udev_device_get_devtype(dev);
        if (!typestr) {
@@ -142,11 +142,18 @@ static int udev_handle_block_add(struct pb_udev *udev, struct udev_device *dev,
                }
        }
 
-       /* Ignore any device mapper devices that aren't logical volumes */
+       /*
+        * Ignore any device mapper devices that aren't logical volumes or
+        * opened encrypted devices
+        */
        devname = udev_device_get_property_value(dev, "DM_NAME");
-       if (devname && ! udev_device_get_property_value(dev, "DM_LV_NAME")) {
-               pb_debug("SKIP: dm-device %s\n", devname);
-               return 0;
+       if (devname) {
+               if (device_handler_found_crypt_device(udev->handler, devname)) {
+                       luks = true;
+               } else if (!udev_device_get_property_value(dev, "DM_LV_NAME")) {
+                       pb_debug("SKIP: dm-device %s\n", devname);
+                       return 0;
+               }
        }
 
        type = udev_device_get_property_value(dev, "ID_FS_TYPE");
@@ -216,16 +223,32 @@ static int udev_handle_block_add(struct pb_udev *udev, struct udev_device *dev,
        usb = !!udev_device_get_property_value(dev, "ID_USB_DRIVER");
        if (cdrom)
                ddev->device->type = DEVICE_TYPE_OPTICAL;
+       else if (strncmp(type, "crypto_LUKS", strlen("crypto_LUKS")) == 0)
+               ddev->device->type = DEVICE_TYPE_LUKS;
        else
                ddev->device->type = usb ? DEVICE_TYPE_USB : DEVICE_TYPE_DISK;
 
        udev_setup_device_params(dev, ddev);
 
+       /*
+        * Don't perform discovery on encrypted devices, just register and
+        * notify clients.
+        */
+       if (ddev->device->type == DEVICE_TYPE_LUKS) {
+               pb_log("Notifying clients about encrypted device %s\n",
+                               name);
+               device_handler_add_encrypted_dev(udev->handler, ddev);
+               return 0;
+       }
+
        /* Create a snapshot for all disk devices */
        if ((ddev->device->type == DEVICE_TYPE_DISK ||
             ddev->device->type == DEVICE_TYPE_USB))
                devmapper_init_snapshot(udev->handler, ddev);
 
+       /* Note if this is an opened LUKS device */
+       ddev->crypt_device = luks;
+
        device_handler_discover(udev->handler, ddev);
 
        return 0;
index a9856b494fe2d29048901690d1e0bb75800178c9..158957010751d50dcf46a2dcfd7be08dc37c6614 100644 (file)
@@ -116,3 +116,9 @@ void discover_server_notify_plugins_remove(struct discover_server *server)
 {
        (void)server;
 }
+
+int devmapper_destroy_snapshot(struct discover_device *device)
+{
+       (void)device;
+       return 0;
+}