From b3ed2ce024c36054e51cca2eb31a1cdbe4a5f11e Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Tue, 4 Dec 2018 10:31:11 -0800 Subject: acpi/nfit: Add support for Intel DSM 1.8 commands Add command definition for security commands defined in Intel DSM specification v1.8 [1]. This includes "get security state", "set passphrase", "unlock unit", "freeze lock", "secure erase", "overwrite", "overwrite query", "master passphrase enable/disable", and "master erase", . Since this adds several Intel definitions, move the relevant bits to their own header. These commands mutate physical data, but that manipulation is not cache coherent. The requirement to flush and invalidate caches makes these commands unsuitable to be called from userspace, so extra logic is added to detect and block these commands from being submitted via the ioctl command submission path. Lastly, the commands may contain sensitive key material that should not be dumped in a standard debug session. Update the nvdimm-command payload-dump facility to move security command payloads behind a default-off compile time switch. [1]: http://pmem.io/documents/NVDIMM_DSM_Interface-V1.8.pdf Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/acpi/nfit/Kconfig | 11 +++++++ drivers/acpi/nfit/core.c | 46 ++++++++++++++++++++++++++--- drivers/acpi/nfit/intel.h | 74 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/nfit/nfit.h | 21 +++++++++++++- drivers/nvdimm/bus.c | 2 +- include/linux/libnvdimm.h | 2 +- 6 files changed, 149 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/nfit/Kconfig b/drivers/acpi/nfit/Kconfig index f7c57e33499e..52eefd732cf2 100644 --- a/drivers/acpi/nfit/Kconfig +++ b/drivers/acpi/nfit/Kconfig @@ -13,3 +13,14 @@ config ACPI_NFIT To compile this driver as a module, choose M here: the module will be called nfit. + +config NFIT_SECURITY_DEBUG + bool "Enable debug for NVDIMM security commands" + depends on ACPI_NFIT + help + Some NVDIMM devices and controllers support encryption and + other security features. The payloads for the commands that + enable those features may contain sensitive clear-text + security material. Disable debug of those command payloads + by default. If you are a kernel developer actively working + on NVDIMM security enabling say Y, otherwise say N. diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 14d9f5bea015..58fb4ce42548 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -24,6 +24,7 @@ #include #include #include +#include "intel.h" #include "nfit.h" #include "intel.h" @@ -380,6 +381,14 @@ static u8 nfit_dsm_revid(unsigned family, unsigned func) [NVDIMM_INTEL_QUERY_FWUPDATE] = 2, [NVDIMM_INTEL_SET_THRESHOLD] = 2, [NVDIMM_INTEL_INJECT_ERROR] = 2, + [NVDIMM_INTEL_GET_SECURITY_STATE] = 2, + [NVDIMM_INTEL_SET_PASSPHRASE] = 2, + [NVDIMM_INTEL_DISABLE_PASSPHRASE] = 2, + [NVDIMM_INTEL_UNLOCK_UNIT] = 2, + [NVDIMM_INTEL_FREEZE_LOCK] = 2, + [NVDIMM_INTEL_SECURE_ERASE] = 2, + [NVDIMM_INTEL_OVERWRITE] = 2, + [NVDIMM_INTEL_QUERY_OVERWRITE] = 2, }, }; u8 id; @@ -394,6 +403,17 @@ static u8 nfit_dsm_revid(unsigned family, unsigned func) return id; } +static bool payload_dumpable(struct nvdimm *nvdimm, unsigned int func) +{ + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + + if (nfit_mem && nfit_mem->family == NVDIMM_FAMILY_INTEL + && func >= NVDIMM_INTEL_GET_SECURITY_STATE + && func <= NVDIMM_INTEL_MASTER_SECURE_ERASE) + return IS_ENABLED(CONFIG_NFIT_SECURITY_DEBUG); + return true; +} + int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc) { @@ -478,9 +498,10 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, dev_dbg(dev, "%s cmd: %d: func: %d input length: %d\n", dimm_name, cmd, func, in_buf.buffer.length); - print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4, - in_buf.buffer.pointer, - min_t(u32, 256, in_buf.buffer.length), true); + if (payload_dumpable(nvdimm, func)) + print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4, + in_buf.buffer.pointer, + min_t(u32, 256, in_buf.buffer.length), true); /* call the BIOS, prefer the named methods over _DSM if available */ if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE @@ -3337,7 +3358,7 @@ static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc) return 0; } -static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, +static int __acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd) { struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc); @@ -3359,6 +3380,23 @@ static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, return 0; } +/* prevent security commands from being issued via ioctl */ +static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, + struct nvdimm *nvdimm, unsigned int cmd, void *buf) +{ + struct nd_cmd_pkg *call_pkg = buf; + unsigned int func; + + if (nvdimm && cmd == ND_CMD_CALL && + call_pkg->nd_family == NVDIMM_FAMILY_INTEL) { + func = call_pkg->nd_command; + if ((1 << func) & NVDIMM_INTEL_SECURITY_CMDMASK) + return -EOPNOTSUPP; + } + + return __acpi_nfit_clear_to_send(nd_desc, nvdimm, cmd); +} + int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, enum nfit_ars_state req_type) { diff --git a/drivers/acpi/nfit/intel.h b/drivers/acpi/nfit/intel.h index 86746312381f..1802bd398c23 100644 --- a/drivers/acpi/nfit/intel.h +++ b/drivers/acpi/nfit/intel.h @@ -35,4 +35,78 @@ struct nd_intel_smart { }; } __packed; +#define ND_INTEL_STATUS_SIZE 4 +#define ND_INTEL_PASSPHRASE_SIZE 32 + +#define ND_INTEL_STATUS_NOT_SUPPORTED 1 +#define ND_INTEL_STATUS_RETRY 5 +#define ND_INTEL_STATUS_NOT_READY 9 +#define ND_INTEL_STATUS_INVALID_STATE 10 +#define ND_INTEL_STATUS_INVALID_PASS 11 +#define ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED 0x10007 +#define ND_INTEL_STATUS_OQUERY_INPROGRESS 0x10007 +#define ND_INTEL_STATUS_OQUERY_SEQUENCE_ERR 0x20007 + +#define ND_INTEL_SEC_STATE_ENABLED 0x02 +#define ND_INTEL_SEC_STATE_LOCKED 0x04 +#define ND_INTEL_SEC_STATE_FROZEN 0x08 +#define ND_INTEL_SEC_STATE_PLIMIT 0x10 +#define ND_INTEL_SEC_STATE_UNSUPPORTED 0x20 +#define ND_INTEL_SEC_STATE_OVERWRITE 0x40 + +#define ND_INTEL_SEC_ESTATE_ENABLED 0x01 +#define ND_INTEL_SEC_ESTATE_PLIMIT 0x02 + +struct nd_intel_get_security_state { + u32 status; + u8 extended_state; + u8 reserved[3]; + u8 state; + u8 reserved1[3]; +} __packed; + +struct nd_intel_set_passphrase { + u8 old_pass[ND_INTEL_PASSPHRASE_SIZE]; + u8 new_pass[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_unlock_unit { + u8 passphrase[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_disable_passphrase { + u8 passphrase[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_freeze_lock { + u32 status; +} __packed; + +struct nd_intel_secure_erase { + u8 passphrase[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_overwrite { + u8 passphrase[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_query_overwrite { + u32 status; +} __packed; + +struct nd_intel_set_master_passphrase { + u8 old_pass[ND_INTEL_PASSPHRASE_SIZE]; + u8 new_pass[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_master_secure_erase { + u8 passphrase[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; #endif diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index df0f6b8407e7..ecde13a9199d 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -60,14 +60,33 @@ enum nvdimm_family_cmds { NVDIMM_INTEL_QUERY_FWUPDATE = 16, NVDIMM_INTEL_SET_THRESHOLD = 17, NVDIMM_INTEL_INJECT_ERROR = 18, + NVDIMM_INTEL_GET_SECURITY_STATE = 19, + NVDIMM_INTEL_SET_PASSPHRASE = 20, + NVDIMM_INTEL_DISABLE_PASSPHRASE = 21, + NVDIMM_INTEL_UNLOCK_UNIT = 22, + NVDIMM_INTEL_FREEZE_LOCK = 23, + NVDIMM_INTEL_SECURE_ERASE = 24, + NVDIMM_INTEL_OVERWRITE = 25, + NVDIMM_INTEL_QUERY_OVERWRITE = 26, + NVDIMM_INTEL_SET_MASTER_PASSPHRASE = 27, + NVDIMM_INTEL_MASTER_SECURE_ERASE = 28, }; +#define NVDIMM_INTEL_SECURITY_CMDMASK \ +(1 << NVDIMM_INTEL_GET_SECURITY_STATE | 1 << NVDIMM_INTEL_SET_PASSPHRASE \ +| 1 << NVDIMM_INTEL_DISABLE_PASSPHRASE | 1 << NVDIMM_INTEL_UNLOCK_UNIT \ +| 1 << NVDIMM_INTEL_FREEZE_LOCK | 1 << NVDIMM_INTEL_SECURE_ERASE \ +| 1 << NVDIMM_INTEL_OVERWRITE | 1 << NVDIMM_INTEL_QUERY_OVERWRITE \ +| 1 << NVDIMM_INTEL_SET_MASTER_PASSPHRASE \ +| 1 << NVDIMM_INTEL_MASTER_SECURE_ERASE) + #define NVDIMM_INTEL_CMDMASK \ (NVDIMM_STANDARD_CMDMASK | 1 << NVDIMM_INTEL_GET_MODES \ | 1 << NVDIMM_INTEL_GET_FWINFO | 1 << NVDIMM_INTEL_START_FWUPDATE \ | 1 << NVDIMM_INTEL_SEND_FWUPDATE | 1 << NVDIMM_INTEL_FINISH_FWUPDATE \ | 1 << NVDIMM_INTEL_QUERY_FWUPDATE | 1 << NVDIMM_INTEL_SET_THRESHOLD \ - | 1 << NVDIMM_INTEL_INJECT_ERROR | 1 << NVDIMM_INTEL_LATCH_SHUTDOWN) + | 1 << NVDIMM_INTEL_INJECT_ERROR | 1 << NVDIMM_INTEL_LATCH_SHUTDOWN \ + | NVDIMM_INTEL_SECURITY_CMDMASK) enum nfit_uuids { /* for simplicity alias the uuid index with the family id */ diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index f1fb39921236..9743d8083538 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -902,7 +902,7 @@ static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus, /* ask the bus provider if it would like to block this request */ if (nd_desc->clear_to_send) { - int rc = nd_desc->clear_to_send(nd_desc, nvdimm, cmd); + int rc = nd_desc->clear_to_send(nd_desc, nvdimm, cmd, data); if (rc) return rc; diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 097072c5a852..472171af7f60 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -87,7 +87,7 @@ struct nvdimm_bus_descriptor { ndctl_fn ndctl; int (*flush_probe)(struct nvdimm_bus_descriptor *nd_desc); int (*clear_to_send)(struct nvdimm_bus_descriptor *nd_desc, - struct nvdimm *nvdimm, unsigned int cmd); + struct nvdimm *nvdimm, unsigned int cmd, void *data); }; struct nd_cmd_desc { -- cgit v1.2.3 From 9bf3aa44644721b287724ecac835d38f1dae4e2d Mon Sep 17 00:00:00 2001 From: Ocean He Date: Fri, 3 Aug 2018 08:08:33 -0400 Subject: libnvdimm, bus: Check id immediately following ida_simple_get The id check was not executed immediately following ida_simple_get. Just change the codes position, without function change. Signed-off-by: Ocean He Reviewed-by: Vishal Verma Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/nvdimm/bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index f1fb39921236..36da259ac9b5 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -344,12 +344,12 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent, INIT_LIST_HEAD(&nvdimm_bus->mapping_list); init_waitqueue_head(&nvdimm_bus->probe_wait); nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL); - mutex_init(&nvdimm_bus->reconfig_mutex); - badrange_init(&nvdimm_bus->badrange); if (nvdimm_bus->id < 0) { kfree(nvdimm_bus); return NULL; } + mutex_init(&nvdimm_bus->reconfig_mutex); + badrange_init(&nvdimm_bus->badrange); nvdimm_bus->nd_desc = nd_desc; nvdimm_bus->dev.parent = parent; nvdimm_bus->dev.release = nvdimm_bus_release; -- cgit v1.2.3 From 9f619d4769a22b1c2f9d8687adbbe4baae12c375 Mon Sep 17 00:00:00 2001 From: Ocean He Date: Tue, 7 Aug 2018 02:15:31 -0400 Subject: ACPI/nfit: Adjust annotation for why return 0 if fail to find NFIT at start Add detailed explanation for why it's ok to return 0 if we fail to find an NFIT at startup. Refer to chapter 9.20.2 NVDIMM Root Device in ACPI 6.2 spec. Signed-off-by: Ocean He Reviewed-by: Vishal Verma Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 5912d30020c7..59dd07c94f12 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -3474,7 +3474,13 @@ static int acpi_nfit_add(struct acpi_device *adev) status = acpi_get_table(ACPI_SIG_NFIT, 0, &tbl); if (ACPI_FAILURE(status)) { - /* This is ok, we could have an nvdimm hotplugged later */ + /* The NVDIMM root device allows OS to trigger enumeration of + * NVDIMMs through NFIT at boot time and re-enumeration at + * root level via the _FIT method during runtime. + * This is ok to return 0 here, we could have an nvdimm + * hotplugged later and evaluate _FIT method which returns + * data in the format of a series of NFIT Structures. + */ dev_dbg(dev, "failed to find NFIT at startup\n"); return 0; } -- cgit v1.2.3 From 9065ed1281a57f8cf84695b4a1e86ee9bd586b01 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 30 Aug 2018 13:32:07 +0300 Subject: libnvdimm, label: Switch to bitmap_zalloc() Switch to bitmap_zalloc() to show clearly what we are allocating. Besides that it returns pointer of bitmap type instead of opaque void *. Signed-off-by: Andy Shevchenko Reviewed-by: Johannes Thumshirn Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/nvdimm/label.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c index 750dbaa6ce82..a11bf4e6b451 100644 --- a/drivers/nvdimm/label.c +++ b/drivers/nvdimm/label.c @@ -944,8 +944,7 @@ static int __blk_label_update(struct nd_region *nd_region, victims = 0; if (old_num_resources) { /* convert old local-label-map to dimm-slot victim-map */ - victim_map = kcalloc(BITS_TO_LONGS(nslot), sizeof(long), - GFP_KERNEL); + victim_map = bitmap_zalloc(nslot, GFP_KERNEL); if (!victim_map) return -ENOMEM; @@ -968,7 +967,7 @@ static int __blk_label_update(struct nd_region *nd_region, /* don't allow updates that consume the last label */ if (nfree - alloc < 0 || nfree - alloc + victims < 1) { dev_info(&nsblk->common.dev, "insufficient label space\n"); - kfree(victim_map); + bitmap_free(victim_map); return -ENOSPC; } /* from here on we need to abort on error */ @@ -1140,7 +1139,7 @@ static int __blk_label_update(struct nd_region *nd_region, out: kfree(old_res_list); - kfree(victim_map); + bitmap_free(victim_map); return rc; abort: -- cgit v1.2.3 From 3d9cbe37c16ffd19eeab6b49a0311bbb999627d8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 11 Jun 2018 16:47:21 +0300 Subject: libnvdimm, namespace: Replace kmemdup() with kstrndup() kstrndup() takes care of '\0' terminator for the strings. Use it here instead of kmemdup() + explicit terminating the input string. Signed-off-by: Andy Shevchenko Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/nvdimm/namespace_devs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index 681af3a8fd62..4b077555ac70 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -270,11 +270,10 @@ static ssize_t __alt_name_store(struct device *dev, const char *buf, if (dev->driver || to_ndns(dev)->claim) return -EBUSY; - input = kmemdup(buf, len + 1, GFP_KERNEL); + input = kstrndup(buf, len, GFP_KERNEL); if (!input) return -ENOMEM; - input[len] = '\0'; pos = strim(input); if (strlen(pos) + 1 > NSLABEL_NAME_LEN) { rc = -EINVAL; -- cgit v1.2.3 From d6548ae4d16dc231dec22860c9c472bcb991fb15 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Tue, 4 Dec 2018 10:31:20 -0800 Subject: acpi/nfit, libnvdimm: Store dimm id as a member to struct nvdimm The generated dimm id is needed for the sysfs attribute as well as being used as the identifier/description for the security key. Since it's constant and should never change, store it as a member of struct nvdimm. As nvdimm_create() continues to grow parameters relative to NFIT driver requirements, do not require other implementations to keep pace. Introduce __nvdimm_create() to carry the new parameters and keep nvdimm_create() with the long standing default api. Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 31 ++++++++++++++++++------------- drivers/acpi/nfit/nfit.h | 3 +++ drivers/nvdimm/dimm_devs.c | 12 +++++++----- drivers/nvdimm/nd-core.h | 1 + include/linux/libnvdimm.h | 17 +++++++++++++---- 5 files changed, 42 insertions(+), 22 deletions(-) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 58fb4ce42548..49b2665088b7 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -1594,18 +1594,10 @@ static DEVICE_ATTR_RO(flags); static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); + struct nvdimm *nvdimm = to_nvdimm(dev); + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); - if (dcr->valid_fields & ACPI_NFIT_CONTROL_MFG_INFO_VALID) - return sprintf(buf, "%04x-%02x-%04x-%08x\n", - be16_to_cpu(dcr->vendor_id), - dcr->manufacturing_location, - be16_to_cpu(dcr->manufacturing_date), - be32_to_cpu(dcr->serial_number)); - else - return sprintf(buf, "%04x-%08x\n", - be16_to_cpu(dcr->vendor_id), - be32_to_cpu(dcr->serial_number)); + return sprintf(buf, "%s\n", nfit_mem->id); } static DEVICE_ATTR_RO(id); @@ -1801,10 +1793,23 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, const guid_t *guid; int i; int family = -1; + struct acpi_nfit_control_region *dcr = nfit_mem->dcr; /* nfit test assumes 1:1 relationship between commands and dsms */ nfit_mem->dsm_mask = acpi_desc->dimm_cmd_force_en; nfit_mem->family = NVDIMM_FAMILY_INTEL; + + if (dcr->valid_fields & ACPI_NFIT_CONTROL_MFG_INFO_VALID) + sprintf(nfit_mem->id, "%04x-%02x-%04x-%08x", + be16_to_cpu(dcr->vendor_id), + dcr->manufacturing_location, + be16_to_cpu(dcr->manufacturing_date), + be32_to_cpu(dcr->serial_number)); + else + sprintf(nfit_mem->id, "%04x-%08x", + be16_to_cpu(dcr->vendor_id), + be32_to_cpu(dcr->serial_number)); + adev = to_acpi_dev(acpi_desc); if (!adev) { /* unit test case */ @@ -1991,10 +1996,10 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) flush = nfit_mem->nfit_flush ? nfit_mem->nfit_flush->flush : NULL; - nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, + nvdimm = __nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, acpi_nfit_dimm_attribute_groups, flags, cmd_mask, flush ? flush->hint_count : 0, - nfit_mem->flush_wpq); + nfit_mem->flush_wpq, &nfit_mem->id[0]); if (!nvdimm) return -ENOMEM; diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index ecde13a9199d..33691aecfcee 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -183,6 +183,8 @@ enum nfit_mem_flags { NFIT_MEM_DIRTY_COUNT, }; +#define NFIT_DIMM_ID_LEN 22 + /* assembled tables for a given dimm/memory-device */ struct nfit_mem { struct nvdimm *nvdimm; @@ -200,6 +202,7 @@ struct nfit_mem { struct list_head list; struct acpi_device *adev; struct acpi_nfit_desc *acpi_desc; + char id[NFIT_DIMM_ID_LEN+1]; struct resource *flush_wpq; unsigned long dsm_mask; unsigned long flags; diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 6c3de2317390..508dd405f84f 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -383,10 +383,10 @@ struct attribute_group nvdimm_attribute_group = { }; EXPORT_SYMBOL_GPL(nvdimm_attribute_group); -struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, - const struct attribute_group **groups, unsigned long flags, - unsigned long cmd_mask, int num_flush, - struct resource *flush_wpq) +struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, + void *provider_data, const struct attribute_group **groups, + unsigned long flags, unsigned long cmd_mask, int num_flush, + struct resource *flush_wpq, const char *dimm_id) { struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL); struct device *dev; @@ -399,6 +399,8 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, kfree(nvdimm); return NULL; } + + nvdimm->dimm_id = dimm_id; nvdimm->provider_data = provider_data; nvdimm->flags = flags; nvdimm->cmd_mask = cmd_mask; @@ -415,7 +417,7 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, return nvdimm; } -EXPORT_SYMBOL_GPL(nvdimm_create); +EXPORT_SYMBOL_GPL(__nvdimm_create); int alias_dpa_busy(struct device *dev, void *data) { diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index 182258f64417..ff26876e6ea3 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -41,6 +41,7 @@ struct nvdimm { atomic_t busy; int id, num_flush; struct resource *flush_wpq; + const char *dimm_id; }; /** diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 472171af7f60..f980046b9588 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -175,10 +175,19 @@ const char *nvdimm_name(struct nvdimm *nvdimm); struct kobject *nvdimm_kobj(struct nvdimm *nvdimm); unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm); void *nvdimm_provider_data(struct nvdimm *nvdimm); -struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, - const struct attribute_group **groups, unsigned long flags, - unsigned long cmd_mask, int num_flush, - struct resource *flush_wpq); +struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, + void *provider_data, const struct attribute_group **groups, + unsigned long flags, unsigned long cmd_mask, int num_flush, + struct resource *flush_wpq, const char *dimm_id); +static inline struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, + void *provider_data, const struct attribute_group **groups, + unsigned long flags, unsigned long cmd_mask, int num_flush, + struct resource *flush_wpq) +{ + return __nvdimm_create(nvdimm_bus, provider_data, groups, flags, + cmd_mask, num_flush, flush_wpq, NULL); +} + const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd); const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd); u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd, -- cgit v1.2.3 From 76ef5e17252789da79db78341851922af0c16181 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Tue, 4 Dec 2018 10:31:27 -0800 Subject: keys: Export lookup_user_key to external users Export lookup_user_key() symbol in order to allow nvdimm passphrase update to retrieve user injected keys. Signed-off-by: Dave Jiang Acked-by: David Howells Signed-off-by: Dan Williams --- include/linux/key.h | 3 +++ security/keys/internal.h | 2 -- security/keys/process_keys.c | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/linux/key.h b/include/linux/key.h index e58ee10f6e58..7099985e35a9 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -346,6 +346,9 @@ static inline key_serial_t key_serial(const struct key *key) extern void key_set_timeout(struct key *, unsigned); +extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, + key_perm_t perm); + /* * The permissions required on a key that we're looking up. */ diff --git a/security/keys/internal.h b/security/keys/internal.h index 74cb0ff42fed..479909b858c7 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -158,8 +158,6 @@ extern struct key *request_key_and_link(struct key_type *type, extern bool lookup_user_key_possessed(const struct key *key, const struct key_match_data *match_data); -extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, - key_perm_t perm); #define KEY_LOOKUP_CREATE 0x01 #define KEY_LOOKUP_PARTIAL 0x02 #define KEY_LOOKUP_FOR_UNLINK 0x04 diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index d5b25e535d3a..ec4fd4531224 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -755,6 +755,7 @@ reget_creds: put_cred(ctx.cred); goto try_again; } +EXPORT_SYMBOL(lookup_user_key); /* * Join the named keyring as the session keyring if possible else attempt to -- cgit v1.2.3 From 9db67581b91d9e9e05c35570ac3f93872e6c84ca Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Tue, 4 Dec 2018 10:31:33 -0800 Subject: keys-encrypted: add nvdimm key format type to encrypted keys Adding nvdimm key format type to encrypted keys in order to limit the size of the key to 32bytes. Signed-off-by: Dave Jiang Acked-by: Mimi Zohar Signed-off-by: Dan Williams --- Documentation/security/keys/trusted-encrypted.rst | 6 ++++- security/keys/encrypted-keys/encrypted.c | 29 ++++++++++++++++------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst index 3bb24e09a332..e8a1c35cd277 100644 --- a/Documentation/security/keys/trusted-encrypted.rst +++ b/Documentation/security/keys/trusted-encrypted.rst @@ -76,7 +76,7 @@ Usage:: Where:: - format:= 'default | ecryptfs' + format:= 'default | ecryptfs | enc32' key-type:= 'trusted' | 'user' @@ -173,3 +173,7 @@ are anticipated. In particular the new format 'ecryptfs' has been defined in in order to use encrypted keys to mount an eCryptfs filesystem. More details about the usage can be found in the file ``Documentation/security/keys/ecryptfs.rst``. + +Another new format 'enc32' has been defined in order to support encrypted keys +with payload size of 32 bytes. This will initially be used for nvdimm security +but may expand to other usages that require 32 bytes payload. diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index d92cbf9687c3..fe0aefd06f83 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -45,6 +45,7 @@ static const char hmac_alg[] = "hmac(sha256)"; static const char blkcipher_alg[] = "cbc(aes)"; static const char key_format_default[] = "default"; static const char key_format_ecryptfs[] = "ecryptfs"; +static const char key_format_enc32[] = "enc32"; static unsigned int ivsize; static int blksize; @@ -54,6 +55,7 @@ static int blksize; #define HASH_SIZE SHA256_DIGEST_SIZE #define MAX_DATA_SIZE 4096 #define MIN_DATA_SIZE 20 +#define KEY_ENC32_PAYLOAD_LEN 32 static struct crypto_shash *hash_tfm; @@ -62,12 +64,13 @@ enum { }; enum { - Opt_error = -1, Opt_default, Opt_ecryptfs + Opt_error = -1, Opt_default, Opt_ecryptfs, Opt_enc32 }; static const match_table_t key_format_tokens = { {Opt_default, "default"}, {Opt_ecryptfs, "ecryptfs"}, + {Opt_enc32, "enc32"}, {Opt_error, NULL} }; @@ -195,6 +198,7 @@ static int datablob_parse(char *datablob, const char **format, key_format = match_token(p, key_format_tokens, args); switch (key_format) { case Opt_ecryptfs: + case Opt_enc32: case Opt_default: *format = p; *master_desc = strsep(&datablob, " \t"); @@ -625,15 +629,22 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key, format_len = (!format) ? strlen(key_format_default) : strlen(format); decrypted_datalen = dlen; payload_datalen = decrypted_datalen; - if (format && !strcmp(format, key_format_ecryptfs)) { - if (dlen != ECRYPTFS_MAX_KEY_BYTES) { - pr_err("encrypted_key: keylen for the ecryptfs format " - "must be equal to %d bytes\n", - ECRYPTFS_MAX_KEY_BYTES); - return ERR_PTR(-EINVAL); + if (format) { + if (!strcmp(format, key_format_ecryptfs)) { + if (dlen != ECRYPTFS_MAX_KEY_BYTES) { + pr_err("encrypted_key: keylen for the ecryptfs format must be equal to %d bytes\n", + ECRYPTFS_MAX_KEY_BYTES); + return ERR_PTR(-EINVAL); + } + decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES; + payload_datalen = sizeof(struct ecryptfs_auth_tok); + } else if (!strcmp(format, key_format_enc32)) { + if (decrypted_datalen != KEY_ENC32_PAYLOAD_LEN) { + pr_err("encrypted_key: enc32 key payload incorrect length: %d\n", + decrypted_datalen); + return ERR_PTR(-EINVAL); + } } - decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES; - payload_datalen = sizeof(struct ecryptfs_auth_tok); } encrypted_datalen = roundup(decrypted_datalen, blksize); -- cgit v1.2.3 From f2989396553a0bd13f4b25f567a3dee3d722ce40 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Wed, 5 Dec 2018 23:39:29 -0800 Subject: acpi/nfit, libnvdimm: Introduce nvdimm_security_ops Some NVDIMMs, like the ones defined by the NVDIMM_FAMILY_INTEL command set, expose a security capability to lock the DIMMs at poweroff and require a passphrase to unlock them. The security model is derived from ATA security. In anticipation of other DIMMs implementing a similar scheme, and to abstract the core security implementation away from the device-specific details, introduce nvdimm_security_ops. Initially only a status retrieval operation, ->state(), is defined, along with the base infrastructure and definitions for future operations. Signed-off-by: Dave Jiang Co-developed-by: Dan Williams Signed-off-by: Dan Williams --- drivers/acpi/nfit/Makefile | 1 + drivers/acpi/nfit/core.c | 13 ++++++++++- drivers/acpi/nfit/intel.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/nfit/intel.h | 2 ++ drivers/nvdimm/bus.c | 6 +++++ drivers/nvdimm/dimm_devs.c | 45 ++++++++++++++++++++++++++++++++++++- drivers/nvdimm/nd-core.h | 13 +++++++++++ include/linux/libnvdimm.h | 27 +++++++++++++++++++++-- tools/testing/nvdimm/Kbuild | 1 + 9 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 drivers/acpi/nfit/intel.c diff --git a/drivers/acpi/nfit/Makefile b/drivers/acpi/nfit/Makefile index a407e769f103..751081c47886 100644 --- a/drivers/acpi/nfit/Makefile +++ b/drivers/acpi/nfit/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_ACPI_NFIT) := nfit.o nfit-y := core.o +nfit-y += intel.o nfit-$(CONFIG_X86_MCE) += mce.o diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 49b2665088b7..41c261ab793e 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -1930,6 +1930,16 @@ static void shutdown_dimm_notify(void *data) mutex_unlock(&acpi_desc->init_mutex); } +static const struct nvdimm_security_ops *acpi_nfit_get_security_ops(int family) +{ + switch (family) { + case NVDIMM_FAMILY_INTEL: + return intel_security_ops; + default: + return NULL; + } +} + static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) { struct nfit_mem *nfit_mem; @@ -1999,7 +2009,8 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) nvdimm = __nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, acpi_nfit_dimm_attribute_groups, flags, cmd_mask, flush ? flush->hint_count : 0, - nfit_mem->flush_wpq, &nfit_mem->id[0]); + nfit_mem->flush_wpq, &nfit_mem->id[0], + acpi_nfit_get_security_ops(nfit_mem->family)); if (!nvdimm) return -ENOMEM; diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c new file mode 100644 index 000000000000..fd7a8f6d2c20 --- /dev/null +++ b/drivers/acpi/nfit/intel.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2018 Intel Corporation. All rights reserved. */ +#include +#include +#include +#include "intel.h" +#include "nfit.h" + +static enum nvdimm_security_state intel_security_state(struct nvdimm *nvdimm) +{ + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_get_security_state cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_GET_SECURITY_STATE, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_out = + sizeof(struct nd_intel_get_security_state), + .nd_fw_size = + sizeof(struct nd_intel_get_security_state), + }, + }; + int rc; + + if (!test_bit(NVDIMM_INTEL_GET_SECURITY_STATE, &nfit_mem->dsm_mask)) + return -ENXIO; + + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + if (nd_cmd.cmd.status) + return -EIO; + + /* check and see if security is enabled and locked */ + if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED) + return -ENXIO; + else if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) { + if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED) + return NVDIMM_SECURITY_LOCKED; + else if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN || + nd_cmd.cmd.state & ND_INTEL_SEC_STATE_PLIMIT) + return NVDIMM_SECURITY_FROZEN; + else + return NVDIMM_SECURITY_UNLOCKED; + } + return NVDIMM_SECURITY_DISABLED; +} + +static const struct nvdimm_security_ops __intel_security_ops = { + .state = intel_security_state, +}; +const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops; diff --git a/drivers/acpi/nfit/intel.h b/drivers/acpi/nfit/intel.h index 1802bd398c23..0aca682ab9d7 100644 --- a/drivers/acpi/nfit/intel.h +++ b/drivers/acpi/nfit/intel.h @@ -35,6 +35,8 @@ struct nd_intel_smart { }; } __packed; +extern const struct nvdimm_security_ops *intel_security_ops; + #define ND_INTEL_STATUS_SIZE 4 #define ND_INTEL_PASSPHRASE_SIZE 32 diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 9743d8083538..eae17d8ee539 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -331,6 +331,12 @@ struct nvdimm_bus *to_nvdimm_bus(struct device *dev) } EXPORT_SYMBOL_GPL(to_nvdimm_bus); +struct nvdimm_bus *nvdimm_to_bus(struct nvdimm *nvdimm) +{ + return to_nvdimm_bus(nvdimm->dev.parent); +} +EXPORT_SYMBOL_GPL(nvdimm_to_bus); + struct nvdimm_bus *nvdimm_bus_register(struct device *parent, struct nvdimm_bus_descriptor *nd_desc) { diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 508dd405f84f..9609b671311b 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -370,23 +370,60 @@ static ssize_t available_slots_show(struct device *dev, } static DEVICE_ATTR_RO(available_slots); +static ssize_t security_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + + switch (nvdimm->sec.state) { + case NVDIMM_SECURITY_DISABLED: + return sprintf(buf, "disabled\n"); + case NVDIMM_SECURITY_UNLOCKED: + return sprintf(buf, "unlocked\n"); + case NVDIMM_SECURITY_LOCKED: + return sprintf(buf, "locked\n"); + case NVDIMM_SECURITY_FROZEN: + return sprintf(buf, "frozen\n"); + case NVDIMM_SECURITY_OVERWRITE: + return sprintf(buf, "overwrite\n"); + } + + return -ENOTTY; +} +static DEVICE_ATTR_RO(security); + static struct attribute *nvdimm_attributes[] = { &dev_attr_state.attr, &dev_attr_flags.attr, &dev_attr_commands.attr, &dev_attr_available_slots.attr, + &dev_attr_security.attr, NULL, }; +static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, typeof(*dev), kobj); + struct nvdimm *nvdimm = to_nvdimm(dev); + + if (a != &dev_attr_security.attr) + return a->mode; + if (nvdimm->sec.state < 0) + return 0; + return a->mode; +} + struct attribute_group nvdimm_attribute_group = { .attrs = nvdimm_attributes, + .is_visible = nvdimm_visible, }; EXPORT_SYMBOL_GPL(nvdimm_attribute_group); struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, const struct attribute_group **groups, unsigned long flags, unsigned long cmd_mask, int num_flush, - struct resource *flush_wpq, const char *dimm_id) + struct resource *flush_wpq, const char *dimm_id, + const struct nvdimm_security_ops *sec_ops) { struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL); struct device *dev; @@ -413,6 +450,12 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, dev->type = &nvdimm_device_type; dev->devt = MKDEV(nvdimm_major, nvdimm->id); dev->groups = groups; + nvdimm->sec.ops = sec_ops; + /* + * Security state must be initialized before device_add() for + * attribute visibility. + */ + nvdimm->sec.state = nvdimm_security_state(nvdimm); nd_device_register(dev); return nvdimm; diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index ff26876e6ea3..1919f5c0d581 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -42,8 +42,21 @@ struct nvdimm { int id, num_flush; struct resource *flush_wpq; const char *dimm_id; + struct { + const struct nvdimm_security_ops *ops; + enum nvdimm_security_state state; + } sec; }; +static inline enum nvdimm_security_state nvdimm_security_state( + struct nvdimm *nvdimm) +{ + if (!nvdimm->sec.ops) + return -ENXIO; + + return nvdimm->sec.ops->state(nvdimm); +} + /** * struct blk_alloc_info - tracking info for BLK dpa scanning * @nd_mapping: blk region mapping boundaries diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index f980046b9588..f4d63f49f7dd 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -155,6 +155,18 @@ static inline struct nd_blk_region_desc *to_blk_region_desc( } +enum nvdimm_security_state { + NVDIMM_SECURITY_DISABLED, + NVDIMM_SECURITY_UNLOCKED, + NVDIMM_SECURITY_LOCKED, + NVDIMM_SECURITY_FROZEN, + NVDIMM_SECURITY_OVERWRITE, +}; + +struct nvdimm_security_ops { + enum nvdimm_security_state (*state)(struct nvdimm *nvdimm); +}; + void badrange_init(struct badrange *badrange); int badrange_add(struct badrange *badrange, u64 addr, u64 length); void badrange_forget(struct badrange *badrange, phys_addr_t start, @@ -165,6 +177,7 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent, struct nvdimm_bus_descriptor *nfit_desc); void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus); struct nvdimm_bus *to_nvdimm_bus(struct device *dev); +struct nvdimm_bus *nvdimm_to_bus(struct nvdimm *nvdimm); struct nvdimm *to_nvdimm(struct device *dev); struct nd_region *to_nd_region(struct device *dev); struct device *nd_region_dev(struct nd_region *nd_region); @@ -178,14 +191,15 @@ void *nvdimm_provider_data(struct nvdimm *nvdimm); struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, const struct attribute_group **groups, unsigned long flags, unsigned long cmd_mask, int num_flush, - struct resource *flush_wpq, const char *dimm_id); + struct resource *flush_wpq, const char *dimm_id, + const struct nvdimm_security_ops *sec_ops); static inline struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, const struct attribute_group **groups, unsigned long flags, unsigned long cmd_mask, int num_flush, struct resource *flush_wpq) { return __nvdimm_create(nvdimm_bus, provider_data, groups, flags, - cmd_mask, num_flush, flush_wpq, NULL); + cmd_mask, num_flush, flush_wpq, NULL, NULL); } const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd); @@ -214,6 +228,15 @@ void nvdimm_flush(struct nd_region *nd_region); int nvdimm_has_flush(struct nd_region *nd_region); int nvdimm_has_cache(struct nd_region *nd_region); +static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf, + unsigned int buf_len, int *cmd_rc) +{ + struct nvdimm_bus *nvdimm_bus = nvdimm_to_bus(nvdimm); + struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus); + + return nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len, cmd_rc); +} + #ifdef CONFIG_ARCH_HAS_PMEM_API #define ARCH_MEMREMAP_PMEM MEMREMAP_WB void arch_wb_cache_pmem(void *addr, size_t size); diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild index 778ceb651000..4a2f3cff2a75 100644 --- a/tools/testing/nvdimm/Kbuild +++ b/tools/testing/nvdimm/Kbuild @@ -37,6 +37,7 @@ obj-$(CONFIG_DEV_DAX) += device_dax.o obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o nfit-y := $(ACPI_SRC)/core.o +nfit-y += $(ACPI_SRC)/intel.o nfit-$(CONFIG_X86_MCE) += $(ACPI_SRC)/mce.o nfit-y += acpi_nfit_test.o nfit-y += config_check.o -- cgit v1.2.3 From 37833fb7989a9d3c3e26354e6878e682c340d718 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 6 Dec 2018 09:14:08 -0800 Subject: acpi/nfit, libnvdimm: Add freeze security support to Intel nvdimm Add support for freeze security on Intel nvdimm. This locks out any changes to security for the DIMM until a hard reset of the DIMM is performed. This is triggered by writing "freeze" to the generic nvdimm/nmemX "security" sysfs attribute. Signed-off-by: Dave Jiang Co-developed-by: Dan Williams Signed-off-by: Dan Williams --- drivers/acpi/nfit/intel.c | 28 ++++++++++++++++++++ drivers/nvdimm/dimm_devs.c | 66 ++++++++++++++++++++++++++++++++++++++++++++-- drivers/nvdimm/nd-core.h | 1 + include/linux/libnvdimm.h | 1 + 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c index fd7a8f6d2c20..f98d680d1a39 100644 --- a/drivers/acpi/nfit/intel.c +++ b/drivers/acpi/nfit/intel.c @@ -48,7 +48,35 @@ static enum nvdimm_security_state intel_security_state(struct nvdimm *nvdimm) return NVDIMM_SECURITY_DISABLED; } +static int intel_security_freeze(struct nvdimm *nvdimm) +{ + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_freeze_lock cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_FREEZE_LOCK, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + }; + int rc; + + if (!test_bit(NVDIMM_INTEL_FREEZE_LOCK, &nfit_mem->dsm_mask)) + return -ENOTTY; + + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + if (nd_cmd.cmd.status) + return -EIO; + return 0; +} + static const struct nvdimm_security_ops __intel_security_ops = { .state = intel_security_state, + .freeze = intel_security_freeze, }; const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops; diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 9609b671311b..8e0bd2ce4dd0 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -390,7 +390,48 @@ static ssize_t security_show(struct device *dev, return -ENOTTY; } -static DEVICE_ATTR_RO(security); + +static ssize_t __security_store(struct device *dev, const char *buf, size_t len) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + ssize_t rc; + + if (atomic_read(&nvdimm->busy)) + return -EBUSY; + + if (sysfs_streq(buf, "freeze")) { + dev_dbg(dev, "freeze\n"); + rc = nvdimm_security_freeze(nvdimm); + } else + return -EINVAL; + + if (rc == 0) + rc = len; + return rc; + +} + +static ssize_t security_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) + +{ + ssize_t rc; + + /* + * Require all userspace triggered security management to be + * done while probing is idle and the DIMM is not in active use + * in any region. + */ + device_lock(dev); + nvdimm_bus_lock(dev); + wait_nvdimm_bus_probe_idle(dev); + rc = __security_store(dev, buf, len); + nvdimm_bus_unlock(dev); + device_unlock(dev); + + return rc; +} +static DEVICE_ATTR_RW(security); static struct attribute *nvdimm_attributes[] = { &dev_attr_state.attr, @@ -410,7 +451,10 @@ static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n) return a->mode; if (nvdimm->sec.state < 0) return 0; - return a->mode; + /* Are there any state mutation ops? */ + if (nvdimm->sec.ops->freeze) + return a->mode; + return 0444; } struct attribute_group nvdimm_attribute_group = { @@ -462,6 +506,24 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, } EXPORT_SYMBOL_GPL(__nvdimm_create); +int nvdimm_security_freeze(struct nvdimm *nvdimm) +{ + int rc; + + WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev)); + + if (!nvdimm->sec.ops || !nvdimm->sec.ops->freeze) + return -EOPNOTSUPP; + + if (nvdimm->sec.state < 0) + return -EIO; + + rc = nvdimm->sec.ops->freeze(nvdimm); + nvdimm->sec.state = nvdimm_security_state(nvdimm); + + return rc; +} + int alias_dpa_busy(struct device *dev, void *data) { resource_size_t map_end, blk_start, new; diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index 1919f5c0d581..15eff40f55f6 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -56,6 +56,7 @@ static inline enum nvdimm_security_state nvdimm_security_state( return nvdimm->sec.ops->state(nvdimm); } +int nvdimm_security_freeze(struct nvdimm *nvdimm); /** * struct blk_alloc_info - tracking info for BLK dpa scanning diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index f4d63f49f7dd..42c815f97c02 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -165,6 +165,7 @@ enum nvdimm_security_state { struct nvdimm_security_ops { enum nvdimm_security_state (*state)(struct nvdimm *nvdimm); + int (*freeze)(struct nvdimm *nvdimm); }; void badrange_init(struct badrange *badrange); -- cgit v1.2.3 From 4c6926a23b76ea23403976290cd45a7a143f6500 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 6 Dec 2018 12:40:01 -0800 Subject: acpi/nfit, libnvdimm: Add unlock of nvdimm support for Intel DIMMs Add support to unlock the dimm via the kernel key management APIs. The passphrase is expected to be pulled from userspace through keyutils. The key management and sysfs attributes are libnvdimm generic. Encrypted keys are used to protect the nvdimm passphrase at rest. The master key can be a trusted-key sealed in a TPM, preferred, or an encrypted-key, more flexible, but more exposure to a potential attacker. Signed-off-by: Dave Jiang Co-developed-by: Dan Williams Reported-by: Randy Dunlap Signed-off-by: Dan Williams --- drivers/acpi/nfit/intel.c | 109 ++++++++++++++++++++++++++++++++ drivers/nvdimm/Kconfig | 5 ++ drivers/nvdimm/Makefile | 1 + drivers/nvdimm/dimm.c | 16 ++++- drivers/nvdimm/nd.h | 8 +++ drivers/nvdimm/security.c | 148 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/libnvdimm.h | 12 ++++ tools/testing/nvdimm/Kbuild | 1 + 8 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 drivers/nvdimm/security.c diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c index f98d680d1a39..38f2cb364853 100644 --- a/drivers/acpi/nfit/intel.c +++ b/drivers/acpi/nfit/intel.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "intel.h" #include "nfit.h" @@ -75,8 +76,116 @@ static int intel_security_freeze(struct nvdimm *nvdimm) return 0; } +static int intel_security_change_key(struct nvdimm *nvdimm, + const struct nvdimm_key_data *old_data, + const struct nvdimm_key_data *new_data) +{ + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_set_passphrase cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_SET_PASSPHRASE, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = ND_INTEL_PASSPHRASE_SIZE * 2, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + }; + int rc; + + if (!test_bit(NVDIMM_INTEL_SET_PASSPHRASE, &nfit_mem->dsm_mask)) + return -ENOTTY; + + if (old_data) + memcpy(nd_cmd.cmd.old_pass, old_data->data, + sizeof(nd_cmd.cmd.old_pass)); + memcpy(nd_cmd.cmd.new_pass, new_data->data, + sizeof(nd_cmd.cmd.new_pass)); + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + + switch (nd_cmd.cmd.status) { + case 0: + return 0; + case ND_INTEL_STATUS_INVALID_PASS: + return -EINVAL; + case ND_INTEL_STATUS_NOT_SUPPORTED: + return -EOPNOTSUPP; + case ND_INTEL_STATUS_INVALID_STATE: + default: + return -EIO; + } +} + +static void nvdimm_invalidate_cache(void); + +static int intel_security_unlock(struct nvdimm *nvdimm, + const struct nvdimm_key_data *key_data) +{ + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_unlock_unit cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_UNLOCK_UNIT, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + }; + int rc; + + if (!test_bit(NVDIMM_INTEL_UNLOCK_UNIT, &nfit_mem->dsm_mask)) + return -ENOTTY; + + memcpy(nd_cmd.cmd.passphrase, key_data->data, + sizeof(nd_cmd.cmd.passphrase)); + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + switch (nd_cmd.cmd.status) { + case 0: + break; + case ND_INTEL_STATUS_INVALID_PASS: + return -EINVAL; + default: + return -EIO; + } + + /* DIMM unlocked, invalidate all CPU caches before we read it */ + nvdimm_invalidate_cache(); + + return 0; +} + +/* + * TODO: define a cross arch wbinvd equivalent when/if + * NVDIMM_FAMILY_INTEL command support arrives on another arch. + */ +#ifdef CONFIG_X86 +static void nvdimm_invalidate_cache(void) +{ + wbinvd_on_all_cpus(); +} +#else +static void nvdimm_invalidate_cache(void) +{ + WARN_ON_ONCE("cache invalidation required after unlock\n"); +} +#endif + static const struct nvdimm_security_ops __intel_security_ops = { .state = intel_security_state, .freeze = intel_security_freeze, + .change_key = intel_security_change_key, +#ifdef CONFIG_X86 + .unlock = intel_security_unlock, +#endif }; + const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops; diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig index 9d36473dc2a2..5e27918e4624 100644 --- a/drivers/nvdimm/Kconfig +++ b/drivers/nvdimm/Kconfig @@ -112,4 +112,9 @@ config OF_PMEM Select Y if unsure. +config NVDIMM_KEYS + def_bool y + depends on ENCRYPTED_KEYS + depends on (LIBNVDIMM=ENCRYPTED_KEYS) || LIBNVDIMM=m + endif diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile index e8847045dac0..6f2a088afad6 100644 --- a/drivers/nvdimm/Makefile +++ b/drivers/nvdimm/Makefile @@ -27,3 +27,4 @@ libnvdimm-$(CONFIG_ND_CLAIM) += claim.o libnvdimm-$(CONFIG_BTT) += btt_devs.o libnvdimm-$(CONFIG_NVDIMM_PFN) += pfn_devs.o libnvdimm-$(CONFIG_NVDIMM_DAX) += dax_devs.o +libnvdimm-$(CONFIG_NVDIMM_KEYS) += security.o diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c index 9899c97138a3..1b3d9e7b2ffe 100644 --- a/drivers/nvdimm/dimm.c +++ b/drivers/nvdimm/dimm.c @@ -34,7 +34,11 @@ static int nvdimm_probe(struct device *dev) return rc; } - /* reset locked, to be validated below... */ + /* + * The locked status bit reflects explicit status codes from the + * label reading commands, revalidate it each time the driver is + * activated and re-reads the label area. + */ nvdimm_clear_locked(dev); ndd = kzalloc(sizeof(*ndd), GFP_KERNEL); @@ -51,6 +55,16 @@ static int nvdimm_probe(struct device *dev) get_device(dev); kref_init(&ndd->kref); + /* + * Attempt to unlock, if the DIMM supports security commands, + * otherwise the locked indication is determined by explicit + * status codes from the label reading commands. + */ + rc = nvdimm_security_unlock(dev); + if (rc < 0) + dev_err(dev, "failed to unlock dimm: %d\n", rc); + + /* * EACCES failures reading the namespace label-area-properties * are interpreted as the DIMM capacity being locked but the diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index e79cc8e5c114..cfde992684e7 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -250,6 +250,14 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, void nvdimm_set_aliasing(struct device *dev); void nvdimm_set_locked(struct device *dev); void nvdimm_clear_locked(struct device *dev); +#if IS_ENABLED(CONFIG_NVDIMM_KEYS) +int nvdimm_security_unlock(struct device *dev); +#else +static inline int nvdimm_security_unlock(struct device *dev) +{ + return 0; +} +#endif struct nd_btt *to_nd_btt(struct device *dev); struct nd_gen_sb { diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c new file mode 100644 index 000000000000..51d77a67a9fb --- /dev/null +++ b/drivers/nvdimm/security.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2018 Intel Corporation. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nd-core.h" +#include "nd.h" + +static bool key_revalidate = true; +module_param(key_revalidate, bool, 0444); +MODULE_PARM_DESC(key_revalidate, "Require key validation at init."); + +static void *key_data(struct key *key) +{ + struct encrypted_key_payload *epayload = dereference_key_locked(key); + + lockdep_assert_held_read(&key->sem); + + return epayload->decrypted_data; +} + +static void nvdimm_put_key(struct key *key) +{ + up_read(&key->sem); + key_put(key); +} + +/* + * Retrieve kernel key for DIMM and request from user space if + * necessary. Returns a key held for read and must be put by + * nvdimm_put_key() before the usage goes out of scope. + */ +static struct key *nvdimm_request_key(struct nvdimm *nvdimm) +{ + struct key *key = NULL; + static const char NVDIMM_PREFIX[] = "nvdimm:"; + char desc[NVDIMM_KEY_DESC_LEN + sizeof(NVDIMM_PREFIX)]; + struct device *dev = &nvdimm->dev; + + sprintf(desc, "%s%s", NVDIMM_PREFIX, nvdimm->dimm_id); + key = request_key(&key_type_encrypted, desc, ""); + if (IS_ERR(key)) { + if (PTR_ERR(key) == -ENOKEY) + dev_warn(dev, "request_key() found no key\n"); + else + dev_warn(dev, "request_key() upcall failed\n"); + key = NULL; + } else { + struct encrypted_key_payload *epayload; + + down_read(&key->sem); + epayload = dereference_key_locked(key); + if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) { + up_read(&key->sem); + key_put(key); + key = NULL; + } + } + + return key; +} + +static struct key *nvdimm_key_revalidate(struct nvdimm *nvdimm) +{ + struct key *key; + int rc; + + if (!nvdimm->sec.ops->change_key) + return NULL; + + key = nvdimm_request_key(nvdimm); + if (!key) + return NULL; + + /* + * Send the same key to the hardware as new and old key to + * verify that the key is good. + */ + rc = nvdimm->sec.ops->change_key(nvdimm, key_data(key), key_data(key)); + if (rc < 0) { + nvdimm_put_key(key); + key = NULL; + } + return key; +} + +static int __nvdimm_security_unlock(struct nvdimm *nvdimm) +{ + struct device *dev = &nvdimm->dev; + struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); + struct key *key = NULL; + int rc; + + /* The bus lock should be held at the top level of the call stack */ + lockdep_assert_held(&nvdimm_bus->reconfig_mutex); + + if (!nvdimm->sec.ops || !nvdimm->sec.ops->unlock + || nvdimm->sec.state < 0) + return -EIO; + + /* + * If the pre-OS has unlocked the DIMM, attempt to send the key + * from request_key() to the hardware for verification. Failure + * to revalidate the key against the hardware results in a + * freeze of the security configuration. I.e. if the OS does not + * have the key, security is being managed pre-OS. + */ + if (nvdimm->sec.state == NVDIMM_SECURITY_UNLOCKED) { + if (!key_revalidate) + return 0; + + key = nvdimm_key_revalidate(nvdimm); + if (!key) + return nvdimm_security_freeze(nvdimm); + } else + key = nvdimm_request_key(nvdimm); + + if (!key) + return -ENOKEY; + + rc = nvdimm->sec.ops->unlock(nvdimm, key_data(key)); + dev_dbg(dev, "key: %d unlock: %s\n", key_serial(key), + rc == 0 ? "success" : "fail"); + + nvdimm_put_key(key); + nvdimm->sec.state = nvdimm_security_state(nvdimm); + return rc; +} + +int nvdimm_security_unlock(struct device *dev) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + int rc; + + nvdimm_bus_lock(dev); + rc = __nvdimm_security_unlock(nvdimm); + nvdimm_bus_unlock(dev); + return rc; +} diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 42c815f97c02..0f0ab276134e 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -163,9 +163,21 @@ enum nvdimm_security_state { NVDIMM_SECURITY_OVERWRITE, }; +#define NVDIMM_PASSPHRASE_LEN 32 +#define NVDIMM_KEY_DESC_LEN 22 + +struct nvdimm_key_data { + u8 data[NVDIMM_PASSPHRASE_LEN]; +}; + struct nvdimm_security_ops { enum nvdimm_security_state (*state)(struct nvdimm *nvdimm); int (*freeze)(struct nvdimm *nvdimm); + int (*change_key)(struct nvdimm *nvdimm, + const struct nvdimm_key_data *old_data, + const struct nvdimm_key_data *new_data); + int (*unlock)(struct nvdimm *nvdimm, + const struct nvdimm_key_data *key_data); }; void badrange_init(struct badrange *badrange); diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild index 4a2f3cff2a75..33ea40777205 100644 --- a/tools/testing/nvdimm/Kbuild +++ b/tools/testing/nvdimm/Kbuild @@ -80,6 +80,7 @@ libnvdimm-$(CONFIG_ND_CLAIM) += $(NVDIMM_SRC)/claim.o libnvdimm-$(CONFIG_BTT) += $(NVDIMM_SRC)/btt_devs.o libnvdimm-$(CONFIG_NVDIMM_PFN) += $(NVDIMM_SRC)/pfn_devs.o libnvdimm-$(CONFIG_NVDIMM_DAX) += $(NVDIMM_SRC)/dax_devs.o +libnvdimm-$(CONFIG_NVDIMM_KEYS) += $(NVDIMM_SRC)/security.o libnvdimm-y += libnvdimm_test.o libnvdimm-y += config_check.o -- cgit v1.2.3 From 03b65b22ada8115a7a7bfdf0789f6a94adfd6070 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 7 Dec 2018 10:33:30 -0700 Subject: acpi/nfit, libnvdimm: Add disable passphrase support to Intel nvdimm. Add support to disable passphrase (security) for the Intel nvdimm. The passphrase used for disabling is pulled from an encrypted-key in the kernel user keyring. The action is triggered by writing "disable " to the sysfs attribute "security". Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/acpi/nfit/intel.c | 41 ++++++++++++++++++++++++++++++ drivers/nvdimm/dimm_devs.c | 47 +++++++++++++++++++++++++++++++--- drivers/nvdimm/nd-core.h | 9 +++++++ drivers/nvdimm/security.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/libnvdimm.h | 2 ++ 5 files changed, 159 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c index 38f2cb364853..bb033b74bff0 100644 --- a/drivers/acpi/nfit/intel.c +++ b/drivers/acpi/nfit/intel.c @@ -163,6 +163,46 @@ static int intel_security_unlock(struct nvdimm *nvdimm, return 0; } +static int intel_security_disable(struct nvdimm *nvdimm, + const struct nvdimm_key_data *key_data) +{ + int rc; + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_disable_passphrase cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_DISABLE_PASSPHRASE, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + }; + + if (!test_bit(NVDIMM_INTEL_DISABLE_PASSPHRASE, &nfit_mem->dsm_mask)) + return -ENOTTY; + + memcpy(nd_cmd.cmd.passphrase, key_data->data, + sizeof(nd_cmd.cmd.passphrase)); + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL); + if (rc < 0) + return rc; + + switch (nd_cmd.cmd.status) { + case 0: + break; + case ND_INTEL_STATUS_INVALID_PASS: + return -EINVAL; + case ND_INTEL_STATUS_INVALID_STATE: + default: + return -ENXIO; + } + + return 0; +} + /* * TODO: define a cross arch wbinvd equivalent when/if * NVDIMM_FAMILY_INTEL command support arrives on another arch. @@ -183,6 +223,7 @@ static const struct nvdimm_security_ops __intel_security_ops = { .state = intel_security_state, .freeze = intel_security_freeze, .change_key = intel_security_change_key, + .disable = intel_security_disable, #ifdef CONFIG_X86 .unlock = intel_security_unlock, #endif diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 8e0bd2ce4dd0..7f42cc4e119b 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -391,24 +391,65 @@ static ssize_t security_show(struct device *dev, return -ENOTTY; } +#define OPS \ + C( OP_FREEZE, "freeze", 1), \ + C( OP_DISABLE, "disable", 2) +#undef C +#define C(a, b, c) a +enum nvdimmsec_op_ids { OPS }; +#undef C +#define C(a, b, c) { b, c } +static struct { + const char *name; + int args; +} ops[] = { OPS }; +#undef C + +#define SEC_CMD_SIZE 32 +#define KEY_ID_SIZE 10 + static ssize_t __security_store(struct device *dev, const char *buf, size_t len) { struct nvdimm *nvdimm = to_nvdimm(dev); ssize_t rc; + char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1], + nkeystr[KEY_ID_SIZE+1]; + unsigned int key, newkey; + int i; if (atomic_read(&nvdimm->busy)) return -EBUSY; - if (sysfs_streq(buf, "freeze")) { + rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s" + " %"__stringify(KEY_ID_SIZE)"s" + " %"__stringify(KEY_ID_SIZE)"s", + cmd, keystr, nkeystr); + if (rc < 1) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(ops); i++) + if (sysfs_streq(cmd, ops[i].name)) + break; + if (i >= ARRAY_SIZE(ops)) + return -EINVAL; + if (ops[i].args > 1) + rc = kstrtouint(keystr, 0, &key); + if (rc >= 0 && ops[i].args > 2) + rc = kstrtouint(nkeystr, 0, &newkey); + if (rc < 0) + return rc; + + if (i == OP_FREEZE) { dev_dbg(dev, "freeze\n"); rc = nvdimm_security_freeze(nvdimm); + } else if (i == OP_DISABLE) { + dev_dbg(dev, "disable %u\n", key); + rc = nvdimm_security_disable(nvdimm, key); } else return -EINVAL; if (rc == 0) rc = len; return rc; - } static ssize_t security_store(struct device *dev, @@ -452,7 +493,7 @@ static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n) if (nvdimm->sec.state < 0) return 0; /* Are there any state mutation ops? */ - if (nvdimm->sec.ops->freeze) + if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable) return a->mode; return 0444; } diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index 15eff40f55f6..d1351c0b1119 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -57,6 +57,15 @@ static inline enum nvdimm_security_state nvdimm_security_state( return nvdimm->sec.ops->state(nvdimm); } int nvdimm_security_freeze(struct nvdimm *nvdimm); +#if IS_ENABLED(CONFIG_NVDIMM_KEYS) +int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid); +#else +static inline int nvdimm_security_disable(struct nvdimm *nvdimm, + unsigned int keyid) +{ + return -EOPNOTSUPP; +} +#endif /** * struct blk_alloc_info - tracking info for BLK dpa scanning diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c index 51d77a67a9fb..647a99dd3182 100644 --- a/drivers/nvdimm/security.c +++ b/drivers/nvdimm/security.c @@ -69,6 +69,36 @@ static struct key *nvdimm_request_key(struct nvdimm *nvdimm) return key; } +static struct key *nvdimm_lookup_user_key(struct nvdimm *nvdimm, + key_serial_t id) +{ + key_ref_t keyref; + struct key *key; + struct encrypted_key_payload *epayload; + struct device *dev = &nvdimm->dev; + + keyref = lookup_user_key(id, 0, 0); + if (IS_ERR(keyref)) + return NULL; + + key = key_ref_to_ptr(keyref); + if (key->type != &key_type_encrypted) { + key_put(key); + return NULL; + } + dev_dbg(dev, "%s: key found: %#x\n", __func__, key_serial(key)); + + + down_read(&key->sem); + epayload = dereference_key_locked(key); + if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) { + up_read(&key->sem); + key_put(key); + key = NULL; + } + return key; +} + static struct key *nvdimm_key_revalidate(struct nvdimm *nvdimm) { struct key *key; @@ -146,3 +176,36 @@ int nvdimm_security_unlock(struct device *