[4/5] discover: Recognise and open LUKS encrypted partitions
diff mbox series

Message ID 20190215003603.16285-5-sam@mendozajonas.com
State Accepted
Headers show
Series
  • Support for dm-crypt LUKS devices
Related show

Commit Message

Samuel Mendoza-Jonas Feb. 15, 2019, 12:36 a.m. UTC
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  | 134 ++++++++++++++++++++++++++++++++++++-
 discover/device-handler.h  |   8 +++
 discover/discover-server.c |  18 ++++-
 discover/udev.c            |  33 +++++++--
 4 files changed, 186 insertions(+), 7 deletions(-)

Patch
diff mbox series

diff --git a/discover/device-handler.c b/discover/device-handler.c
index e75f4123..7935a2b8 100644
--- a/discover/device-handler.c
+++ b/discover/device-handler.c
@@ -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,102 @@  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;
+
+	if (process->exit_status) {
+		/* failed to open the device; stop tracking it */
+		device_handler_found_crypt_device(handler, process->argv[3]);
+		device_handler_status_err(handler,
+				_("Failed to open encrypted device %s"),
+				process->argv[2]);
+	}
+}
+
+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;
+	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);
+
+	const char *argv[] = {
+		pb_system_apps.cryptsetup,
+		"luksOpen",
+		device_path,
+		name,
+		"-",
+		NULL
+	};
+
+	p = process_create(handler);
+	p->path = pb_system_apps.cryptsetup;
+	p->argv = argv;
+	p->exit_cb = cryptsetup_cb;
+	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;
+	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 +2254,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);
 
diff --git a/discover/device-handler.h b/discover/device-handler.h
index 9619a2df..65911208 100644
--- a/discover/device-handler.h
+++ b/discover/device-handler.h
@@ -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,
diff --git a/discover/discover-server.c b/discover/discover-server.c
index 23d6113e..1a332cbf 100644
--- a/discover/discover-server.c
+++ b/discover/discover-server.c
@@ -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;
diff --git a/discover/udev.c b/discover/udev.c
index fa5d4b41..0c3da66a 100644
--- a/discover/udev.c
+++ b/discover/udev.c
@@ -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;