Linux-Raid Archives mirror
 help / color / mirror / Atom feed
From: Xiao Ni <xni@redhat.com>
To: Blazej Kucman <blazej.kucman@intel.com>
Cc: mariusz.tkaczyk@linux.intel.com, jes@trained-monkey.org,
	 linux-raid@vger.kernel.org
Subject: Re: [PATCH v2 2/6] Add reading Opal NVMe encryption information
Date: Wed, 15 May 2024 11:20:33 +0800	[thread overview]
Message-ID: <CALTww2_=gHgut3M=q6SqjenG1oS3FAmG5N+FdQ0fahexZ39PSg@mail.gmail.com> (raw)
In-Reply-To: <20240322115120.12325-3-blazej.kucman@intel.com>

On Fri, Mar 22, 2024 at 7:52 PM Blazej Kucman <blazej.kucman@intel.com> wrote:
>
> 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>
> ---
>  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;

Hi all

In i686 platform, it reports error:

drive_encryption.c: In function ‘nvme_security_recv_ioctl’:
drive_encryption.c:236:25: error: cast from pointer to integer of
different size [-Werror=pointer-to-int-cast]
  236 |         nvme_cmd.addr = (__u64)response_buffer;
      |                         ^
drive_encryption.c: In function ‘nvme_identify_ioctl’:
drive_encryption.c:271:25: error: cast from pointer to integer of
different size [-Werror=pointer-to-int-cast]
  271 |         nvme_cmd.addr = (__u64)response_buffer;
      |                         ^
cc1: all warnings being treated as errors
make: *** [Makefile:211: drive_encryption.o] Error 1

The pointer should be 32bit and it tries to convert to 64 bit.

Best Regards
Xiao

> +
> +       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.35.3
>
>


  reply	other threads:[~2024-05-15  3:20 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-22 11:51 [PATCH v2 0/6] Disk encryption status handling Blazej Kucman
2024-03-22 11:51 ` [PATCH v2 1/6] mdadm: Move pr_vrb define to mdadm.h Blazej Kucman
2024-03-22 11:51 ` [PATCH v2 2/6] Add reading Opal NVMe encryption information Blazej Kucman
2024-05-15  3:20   ` Xiao Ni [this message]
2024-05-15 14:26     ` Blazej Kucman
2024-05-16  9:02       ` Blazej Kucman
2024-03-22 11:51 ` [PATCH v2 3/6] Add reading SATA " Blazej Kucman
2024-03-22 11:51 ` [PATCH v2 4/6] Add key ENCRYPTION_NO_VERIFY to conf Blazej Kucman
2024-03-22 11:51 ` [PATCH v2 5/6] imsm: print disk encryption information Blazej Kucman
2024-03-22 11:51 ` [PATCH v2 6/6] imsm: drive encryption policy implementation Blazej Kucman
2024-04-02  6:32 ` [PATCH v2 0/6] Disk encryption status handling Mariusz Tkaczyk

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CALTww2_=gHgut3M=q6SqjenG1oS3FAmG5N+FdQ0fahexZ39PSg@mail.gmail.com' \
    --to=xni@redhat.com \
    --cc=blazej.kucman@intel.com \
    --cc=jes@trained-monkey.org \
    --cc=linux-raid@vger.kernel.org \
    --cc=mariusz.tkaczyk@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).