464 lines
15 KiB
Diff
464 lines
15 KiB
Diff
From cc48406887b3bc439e3462e8e4d20f992e81b87e Mon Sep 17 00:00:00 2001
|
|
From: Blazej Kucman <blazej.kucman@intel.com>
|
|
Date: Fri, 22 Mar 2024 12:51:16 +0100
|
|
Subject: [PATCH 43/69] Add reading Opal NVMe encryption information
|
|
|
|
For NVMe devices with Opal support, encryption information, status and
|
|
ability are determined based on Opal Level 0 discovery response. Technical
|
|
documentation used is given in the implementation.
|
|
|
|
Ability in general describes what type of encryption is supported, Status
|
|
describes in what state the disk with encryption support is. The current
|
|
patch includes only the implementation of reading encryption information,
|
|
functions will be used in one of the next patches.
|
|
|
|
Motivation for adding this functionality is to block mixing of disks in
|
|
IMSM arrays with encryption enabled and disabled. The main goal is to not
|
|
allow stealing data by rebuilding array to not encrypted drive which can be
|
|
read elsewhere.
|
|
|
|
Value ENA_OTHER from enum encryption_ability will be used in the next
|
|
patch.
|
|
|
|
Signed-off-by: Blazej Kucman <blazej.kucman@intel.com>
|
|
Signed-off-by: Mariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
|
|
---
|
|
Makefile | 4 +-
|
|
drive_encryption.c | 362 +++++++++++++++++++++++++++++++++++++++++++++
|
|
drive_encryption.h | 32 ++++
|
|
3 files changed, 396 insertions(+), 2 deletions(-)
|
|
create mode 100644 drive_encryption.c
|
|
create mode 100644 drive_encryption.h
|
|
|
|
diff --git a/Makefile b/Makefile
|
|
index cbdba49a..7c221a89 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -170,7 +170,7 @@ OBJS = mdadm.o config.o policy.o mdstat.o ReadMe.o uuid.o util.o maps.o lib.o u
|
|
mdopen.o super0.o super1.o super-ddf.o super-intel.o bitmap.o \
|
|
super-mbr.o super-gpt.o \
|
|
restripe.o sysfs.o sha1.o mapfile.o crc32.o sg_io.o msg.o xmalloc.o \
|
|
- platform-intel.o probe_roms.o crc32c.o
|
|
+ platform-intel.o probe_roms.o crc32c.o drive_encryption.o
|
|
|
|
CHECK_OBJS = restripe.o uuid.o sysfs.o maps.o lib.o xmalloc.o dlink.o
|
|
|
|
@@ -183,7 +183,7 @@ MON_OBJS = mdmon.o monitor.o managemon.o uuid.o util.o maps.o mdstat.o sysfs.o c
|
|
Kill.o sg_io.o dlink.o ReadMe.o super-intel.o \
|
|
super-mbr.o super-gpt.o \
|
|
super-ddf.o sha1.o crc32.o msg.o bitmap.o xmalloc.o \
|
|
- platform-intel.o probe_roms.o crc32c.o
|
|
+ platform-intel.o probe_roms.o crc32c.o drive_encryption.o
|
|
|
|
MON_SRCS = $(patsubst %.o,%.c,$(MON_OBJS))
|
|
|
|
diff --git a/drive_encryption.c b/drive_encryption.c
|
|
new file mode 100644
|
|
index 00000000..b44585a7
|
|
--- /dev/null
|
|
+++ b/drive_encryption.c
|
|
@@ -0,0 +1,362 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-only
|
|
+/*
|
|
+ * Read encryption information for Opal and ATA devices.
|
|
+ *
|
|
+ * Copyright (C) 2024 Intel Corporation
|
|
+ * Author: Blazej Kucman <blazej.kucman@intel.com>
|
|
+ */
|
|
+
|
|
+#include "mdadm.h"
|
|
+
|
|
+#include <asm/types.h>
|
|
+#include <linux/nvme_ioctl.h>
|
|
+#include "drive_encryption.h"
|
|
+
|
|
+/*
|
|
+ * Opal defines
|
|
+ * TCG Storage Opal SSC 2.01 chapter 3.3.3
|
|
+ * NVM ExpressTM Revision 1.4c, chapter 5
|
|
+ */
|
|
+#define TCG_SECP_01 (0x01)
|
|
+#define TCG_SECP_00 (0x00)
|
|
+#define OPAL_DISCOVERY_COMID (0x0001)
|
|
+#define OPAL_LOCKING_FEATURE (0x0002)
|
|
+#define OPAL_IO_BUFFER_LEN 2048
|
|
+#define OPAL_DISCOVERY_FEATURE_HEADER_LEN (4)
|
|
+
|
|
+/*
|
|
+ * NVMe defines
|
|
+ * NVM ExpressTM Revision 1.4c, chapter 5
|
|
+ */
|
|
+#define NVME_SECURITY_RECV (0x82)
|
|
+#define NVME_IDENTIFY (0x06)
|
|
+#define NVME_IDENTIFY_RESPONSE_LEN 4096
|
|
+#define NVME_OACS_BYTE_POSITION (256)
|
|
+#define NVME_IDENTIFY_CONTROLLER_DATA (1)
|
|
+
|
|
+typedef enum drive_feature_support_status {
|
|
+ /* Drive feature is supported. */
|
|
+ DRIVE_FEAT_SUP_ST = 0,
|
|
+ /* Drive feature is not supported. */
|
|
+ DRIVE_FEAT_NOT_SUP_ST,
|
|
+ /* Drive feature support check failed. */
|
|
+ DRIVE_FEAT_CHECK_FAILED_ST
|
|
+} drive_feat_sup_st;
|
|
+
|
|
+/* TCG Storage Opal SSC 2.01 chapter 3.1.1.3 */
|
|
+typedef struct opal_locking_feature {
|
|
+ /* feature header */
|
|
+ __u16 feature_code;
|
|
+ __u8 reserved : 4;
|
|
+ __u8 version : 4;
|
|
+ __u8 description_length;
|
|
+ /* feature description */
|
|
+ __u8 locking_supported : 1;
|
|
+ __u8 locking_enabled : 1;
|
|
+ __u8 locked : 1;
|
|
+ __u8 media_encryption : 1;
|
|
+ __u8 mbr_enabled : 1;
|
|
+ __u8 mbr_done : 1;
|
|
+ __u8 mbr_shadowing_not_supported : 1;
|
|
+ __u8 hw_reset_for_dor_supported : 1;
|
|
+ __u8 reserved1[11];
|
|
+} __attribute__((__packed__)) opal_locking_feature_t;
|
|
+
|
|
+/* TCG Storage Opal SSC 2.01 chapter 3.1.1.1 */
|
|
+typedef struct opal_level0_header {
|
|
+ __u32 length;
|
|
+ __u32 version;
|
|
+ __u64 reserved;
|
|
+ __u8 vendor_specific[32];
|
|
+} opal_level0_header_t;
|
|
+
|
|
+/**
|
|
+ * NVM ExpressTM Revision 1.4c, Figure 249
|
|
+ * Structure specifies only OACS filed, which is needed in the current use case.
|
|
+ */
|
|
+typedef struct nvme_identify_ctrl {
|
|
+ __u8 reserved[255];
|
|
+ __u16 oacs;
|
|
+ __u8 reserved2[3839];
|
|
+} nvme_identify_ctrl_t;
|
|
+
|
|
+/* SCSI Primary Commands - 4 (SPC-4), Table 512 */
|
|
+typedef struct supported_security_protocols {
|
|
+ __u8 reserved[6];
|
|
+ __u16 list_length;
|
|
+ __u8 list[504];
|
|
+} supported_security_protocols_t;
|
|
+
|
|
+/**
|
|
+ * get_opal_locking_feature_description() - get opal locking feature description.
|
|
+ * @response: response from Opal Discovery Level 0.
|
|
+ *
|
|
+ * Based on the documentation TCG Storage Opal SSC 2.01 chapter 3.1.1,
|
|
+ * a Locking feature is searched for in Opal Level 0 Discovery response.
|
|
+ *
|
|
+ * Return: if locking feature is found, pointer to struct %opal_locking_feature_t, NULL otherwise.
|
|
+ */
|
|
+static opal_locking_feature_t *get_opal_locking_feature_description(__u8 *response)
|
|
+{
|
|
+ opal_level0_header_t *response_header = (opal_level0_header_t *)response;
|
|
+ int features_length = __be32_to_cpu(response_header->length);
|
|
+ int current_position = sizeof(*response_header);
|
|
+
|
|
+ while (current_position < features_length) {
|
|
+ opal_locking_feature_t *feature;
|
|
+
|
|
+ feature = (opal_locking_feature_t *)(response + current_position);
|
|
+
|
|
+ if (__be16_to_cpu(feature->feature_code) == OPAL_LOCKING_FEATURE)
|
|
+ return feature;
|
|
+
|
|
+ current_position += feature->description_length + OPAL_DISCOVERY_FEATURE_HEADER_LEN;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * nvme_security_recv_ioctl() - nvme security receive ioctl.
|
|
+ * @disk_fd: a disk file descriptor.
|
|
+ * @sec_protocol: security protocol.
|
|
+ * @comm_id: command id.
|
|
+ * @response_buffer: response buffer to fill out.
|
|
+ * @buf_size: response buffer size.
|
|
+ * @verbose: verbose flag.
|
|
+ *
|
|
+ * Based on the documentations TCG Storage Opal SSC 2.01 chapter 3.3.3 and
|
|
+ * NVM ExpressTM Revision 1.4c, chapter 5.25,
|
|
+ * read security receive command via ioctl().
|
|
+ * On success, @response_buffer is completed.
|
|
+ *
|
|
+ * Return: %MDADM_STATUS_SUCCESS on success, %MDADM_STATUS_ERROR otherwise.
|
|
+ */
|
|
+static mdadm_status_t
|
|
+nvme_security_recv_ioctl(int disk_fd, __u8 sec_protocol, __u16 comm_id, void *response_buffer,
|
|
+ size_t buf_size, const int verbose)
|
|
+{
|
|
+ struct nvme_admin_cmd nvme_cmd = {0};
|
|
+ int status;
|
|
+
|
|
+ nvme_cmd.opcode = NVME_SECURITY_RECV;
|
|
+ nvme_cmd.cdw10 = sec_protocol << 24 | comm_id << 8;
|
|
+ nvme_cmd.cdw11 = buf_size;
|
|
+ nvme_cmd.data_len = buf_size;
|
|
+ nvme_cmd.addr = (__u64)response_buffer;
|
|
+
|
|
+ status = ioctl(disk_fd, NVME_IOCTL_ADMIN_CMD, &nvme_cmd);
|
|
+ if (status != 0) {
|
|
+ pr_vrb("Failed to read NVMe security receive ioctl() for device /dev/%s, status: %d\n",
|
|
+ fd2kname(disk_fd), status);
|
|
+ return MDADM_STATUS_ERROR;
|
|
+ }
|
|
+
|
|
+ return MDADM_STATUS_SUCCESS;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * nvme_identify_ioctl() - NVMe identify ioctl.
|
|
+ * @disk_fd: a disk file descriptor.
|
|
+ * @response_buffer: response buffer to fill out.
|
|
+ * @buf_size: response buffer size.
|
|
+ * @verbose: verbose flag.
|
|
+ *
|
|
+ * Based on the documentations TCG Storage Opal SSC 2.01 chapter 3.3.3 and
|
|
+ * NVM ExpressTM Revision 1.4c, chapter 5.25,
|
|
+ * read NVMe identify via ioctl().
|
|
+ * On success, @response_buffer will be completed.
|
|
+ *
|
|
+ * Return: %MDADM_STATUS_SUCCESS on success, %MDADM_STATUS_ERROR otherwise.
|
|
+ */
|
|
+static mdadm_status_t
|
|
+nvme_identify_ioctl(int disk_fd, void *response_buffer, size_t buf_size, const int verbose)
|
|
+{
|
|
+ struct nvme_admin_cmd nvme_cmd = {0};
|
|
+ int status;
|
|
+
|
|
+ nvme_cmd.opcode = NVME_IDENTIFY;
|
|
+ nvme_cmd.cdw10 = NVME_IDENTIFY_CONTROLLER_DATA;
|
|
+ nvme_cmd.data_len = buf_size;
|
|
+ nvme_cmd.addr = (__u64)response_buffer;
|
|
+
|
|
+ status = ioctl(disk_fd, NVME_IOCTL_ADMIN_CMD, &nvme_cmd);
|
|
+ if (status != 0) {
|
|
+ pr_vrb("Failed to read NVMe identify ioctl() for device /dev/%s, status: %d\n",
|
|
+ fd2kname(disk_fd), status);
|
|
+ return MDADM_STATUS_ERROR;
|
|
+ }
|
|
+
|
|
+ return MDADM_STATUS_SUCCESS;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * is_sec_prot_01h_supported() - check if security protocol 01h supported.
|
|
+ * @security_protocols: struct with response from disk (NVMe, SATA) describing supported
|
|
+ * security protocols.
|
|
+ *
|
|
+ * Return: true if TCG_SECP_01 found, false otherwise.
|
|
+ */
|
|
+static bool is_sec_prot_01h_supported(supported_security_protocols_t *security_protocols)
|
|
+{
|
|
+ int list_length = be16toh(security_protocols->list_length);
|
|
+ int index;
|
|
+
|
|
+ for (index = 0 ; index < list_length; index++) {
|
|
+ if (security_protocols->list[index] == TCG_SECP_01)
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * is_sec_prot_01h_supported_nvme() - check if security protocol 01h supported for given NVMe disk.
|
|
+ * @disk_fd: a disk file descriptor.
|
|
+ * @verbose: verbose flag.
|
|
+ *
|
|
+ * Return: %DRIVE_FEAT_SUP_ST if TCG_SECP_01 supported, %DRIVE_FEAT_NOT_SUP_ST if not supported,
|
|
+ * %DRIVE_FEAT_CHECK_FAILED_ST if failed to check.
|
|
+ */
|
|
+static drive_feat_sup_st is_sec_prot_01h_supported_nvme(int disk_fd, const int verbose)
|
|
+{
|
|
+ supported_security_protocols_t security_protocols = {0};
|
|
+
|
|
+ /* security_protocol: TCG_SECP_00, comm_id: not applicable */
|
|
+ if (nvme_security_recv_ioctl(disk_fd, TCG_SECP_00, 0x0, &security_protocols,
|
|
+ sizeof(security_protocols), verbose))
|
|
+ return DRIVE_FEAT_CHECK_FAILED_ST;
|
|
+
|
|
+ if (is_sec_prot_01h_supported(&security_protocols))
|
|
+ return DRIVE_FEAT_SUP_ST;
|
|
+
|
|
+ return DRIVE_FEAT_NOT_SUP_ST;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * is_nvme_sec_send_recv_supported() - check if Security Send and Security Receive is supported.
|
|
+ * @disk_fd: a disk file descriptor.
|
|
+ * @verbose: verbose flag.
|
|
+ *
|
|
+ * Check if "Optional Admin Command Support" bit 0 is set in NVMe identify.
|
|
+ * Bit 0 set to 1 means controller supports the Security Send and Security Receive commands.
|
|
+ *
|
|
+ * Return: %DRIVE_FEAT_SUP_ST if security send/receive supported,
|
|
+ * %DRIVE_FEAT_NOT_SUP_ST if not supported, %DRIVE_FEAT_CHECK_FAILED_ST if check failed.
|
|
+ */
|
|
+static drive_feat_sup_st is_nvme_sec_send_recv_supported(int disk_fd, const int verbose)
|
|
+{
|
|
+ nvme_identify_ctrl_t nvme_identify = {0};
|
|
+ int status = 0;
|
|
+
|
|
+ status = nvme_identify_ioctl(disk_fd, &nvme_identify, sizeof(nvme_identify), verbose);
|
|
+ if (status)
|
|
+ return DRIVE_FEAT_CHECK_FAILED_ST;
|
|
+
|
|
+ if ((__le16_to_cpu(nvme_identify.oacs) & 0x1) == 0x1)
|
|
+ return DRIVE_FEAT_SUP_ST;
|
|
+
|
|
+ return DRIVE_FEAT_NOT_SUP_ST;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * get_opal_encryption_information() - get Opal encryption information.
|
|
+ * @buffer: buffer with Opal Level 0 Discovery response.
|
|
+ * @information: struct to fill out, describing encryption status of disk.
|
|
+ *
|
|
+ * If Locking feature frame is in response from Opal Level 0 discovery, &encryption_information_t
|
|
+ * structure is completed with status and ability otherwise the status is set to &None.
|
|
+ * For possible encryption statuses and abilities,
|
|
+ * please refer to enums &encryption_status and &encryption_ability.
|
|
+ *
|
|
+ * Return: %MDADM_STATUS_SUCCESS on success, %MDADM_STATUS_ERROR otherwise.
|
|
+ */
|
|
+static mdadm_status_t get_opal_encryption_information(__u8 *buffer,
|
|
+ encryption_information_t *information)
|
|
+{
|
|
+ opal_locking_feature_t *opal_locking_feature =
|
|
+ get_opal_locking_feature_description(buffer);
|
|
+
|
|
+ if (!opal_locking_feature)
|
|
+ return MDADM_STATUS_ERROR;
|
|
+
|
|
+ if (opal_locking_feature->locking_supported == 1) {
|
|
+ information->ability = ENC_ABILITY_SED;
|
|
+
|
|
+ if (opal_locking_feature->locking_enabled == 0)
|
|
+ information->status = ENC_STATUS_UNENCRYPTED;
|
|
+ else if (opal_locking_feature->locked == 1)
|
|
+ information->status = ENC_STATUS_LOCKED;
|
|
+ else
|
|
+ information->status = ENC_STATUS_UNLOCKED;
|
|
+ } else {
|
|
+ information->ability = ENC_ABILITY_NONE;
|
|
+ information->status = ENC_STATUS_UNENCRYPTED;
|
|
+ }
|
|
+
|
|
+ return MDADM_STATUS_SUCCESS;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * get_nvme_opal_encryption_information() - get NVMe Opal encryption information.
|
|
+ * @disk_fd: a disk file descriptor.
|
|
+ * @information: struct to fill out, describing encryption status of disk.
|
|
+ * @verbose: verbose flag.
|
|
+ *
|
|
+ * In case the disk supports Opal Level 0 discovery, &encryption_information_t structure
|
|
+ * is completed with status and ability based on ioctl response,
|
|
+ * otherwise the ability is set to %ENC_ABILITY_NONE and &status to %ENC_STATUS_UNENCRYPTED.
|
|
+ * As the current use case does not need the knowledge of Opal support, if there is no support,
|
|
+ * %MDADM_STATUS_SUCCESS will be returned, with the values described above.
|
|
+ * For possible encryption statuses and abilities,
|
|
+ * please refer to enums &encryption_status and &encryption_ability.
|
|
+ *
|
|
+ * %MDADM_STATUS_SUCCESS on success, %MDADM_STATUS_ERROR otherwise.
|
|
+ */
|
|
+mdadm_status_t
|
|
+get_nvme_opal_encryption_information(int disk_fd, encryption_information_t *information,
|
|
+ const int verbose)
|
|
+{
|
|
+ __u8 buffer[OPAL_IO_BUFFER_LEN];
|
|
+ int sec_send_recv_supported = 0;
|
|
+ int protocol_01h_supported = 0;
|
|
+ mdadm_status_t status;
|
|
+
|
|
+ information->ability = ENC_ABILITY_NONE;
|
|
+ information->status = ENC_STATUS_UNENCRYPTED;
|
|
+
|
|
+ sec_send_recv_supported = is_nvme_sec_send_recv_supported(disk_fd, verbose);
|
|
+ if (sec_send_recv_supported == DRIVE_FEAT_CHECK_FAILED_ST)
|
|
+ return MDADM_STATUS_ERROR;
|
|
+
|
|
+ /* Opal not supported */
|
|
+ if (sec_send_recv_supported == DRIVE_FEAT_NOT_SUP_ST)
|
|
+ return MDADM_STATUS_SUCCESS;
|
|
+
|
|
+ /**
|
|
+ * sec_send_recv_supported determine that it should be possible to read
|
|
+ * supported sec protocols
|
|
+ */
|
|
+ protocol_01h_supported = is_sec_prot_01h_supported_nvme(disk_fd, verbose);
|
|
+ if (protocol_01h_supported == DRIVE_FEAT_CHECK_FAILED_ST)
|
|
+ return MDADM_STATUS_ERROR;
|
|
+
|
|
+ /* Opal not supported */
|
|
+ if (sec_send_recv_supported == DRIVE_FEAT_SUP_ST &&
|
|
+ protocol_01h_supported == DRIVE_FEAT_NOT_SUP_ST)
|
|
+ return MDADM_STATUS_SUCCESS;
|
|
+
|
|
+ if (nvme_security_recv_ioctl(disk_fd, TCG_SECP_01, OPAL_DISCOVERY_COMID, (void *)&buffer,
|
|
+ OPAL_IO_BUFFER_LEN, verbose))
|
|
+ return MDADM_STATUS_ERROR;
|
|
+
|
|
+ status = get_opal_encryption_information((__u8 *)&buffer, information);
|
|
+ if (status)
|
|
+ pr_vrb("Locking feature description not found in Level 0 discovery response. Device /dev/%s.\n",
|
|
+ fd2kname(disk_fd));
|
|
+
|
|
+ if (information->ability == ENC_ABILITY_NONE)
|
|
+ assert(information->status == ENC_STATUS_UNENCRYPTED);
|
|
+
|
|
+ return status;
|
|
+}
|
|
diff --git a/drive_encryption.h b/drive_encryption.h
|
|
new file mode 100644
|
|
index 00000000..82c2c624
|
|
--- /dev/null
|
|
+++ b/drive_encryption.h
|
|
@@ -0,0 +1,32 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-only */
|
|
+/*
|
|
+ * Read encryption information for Opal and ATA devices.
|
|
+ *
|
|
+ * Copyright (C) 2024 Intel Corporation
|
|
+ * Author: Blazej Kucman <blazej.kucman@intel.com>
|
|
+ */
|
|
+
|
|
+typedef enum encryption_status {
|
|
+ /* The drive is not currently encrypted. */
|
|
+ ENC_STATUS_UNENCRYPTED = 0,
|
|
+ /* The drive is encrypted and the data is not accessible. */
|
|
+ ENC_STATUS_LOCKED,
|
|
+ /* The drive is encrypted but the data is accessible in unencrypted form. */
|
|
+ ENC_STATUS_UNLOCKED
|
|
+} encryption_status_t;
|
|
+
|
|
+typedef enum encryption_ability {
|
|
+ ENC_ABILITY_NONE = 0,
|
|
+ ENC_ABILITY_OTHER,
|
|
+ /* Self encrypted drive */
|
|
+ ENC_ABILITY_SED
|
|
+} encryption_ability_t;
|
|
+
|
|
+typedef struct encryption_information {
|
|
+ encryption_ability_t ability;
|
|
+ encryption_status_t status;
|
|
+} encryption_information_t;
|
|
+
|
|
+mdadm_status_t
|
|
+get_nvme_opal_encryption_information(int disk_fd, struct encryption_information *information,
|
|
+ const int verbose);
|
|
--
|
|
2.41.0
|
|
|