From f583f0cf35fc227db5f73ecd04daf7702735b740 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Fri, 15 Feb 2019 10:40:14 +1100 Subject: discover: Recognise and open LUKS encrypted partitions 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 --- discover/device-handler.c | 148 ++++++++++++++++++++++++++++++++++++++++++++- discover/device-handler.h | 8 +++ discover/discover-server.c | 18 +++++- discover/udev.c | 33 ++++++++-- 4 files changed, 200 insertions(+), 7 deletions(-) (limited to 'discover') diff --git a/discover/device-handler.c b/discover/device-handler.c index e75f412..d41bb4b 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,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); diff --git a/discover/device-handler.h b/discover/device-handler.h index 9619a2d..6591120 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 23d6113..1a332cb 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 fa5d4b4..0c3da66 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; -- cgit v1.2.1