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 --- include/linux/libnvdimm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') 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.1 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 --- include/linux/libnvdimm.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'include') 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.1 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 +++ 1 file changed, 3 insertions(+) (limited to 'include') 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. */ -- cgit v1.2.1 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 --- include/linux/libnvdimm.h | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'include') 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); -- cgit v1.2.1 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 --- include/linux/libnvdimm.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') 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.1 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 --- include/linux/libnvdimm.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') 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); -- cgit v1.2.1 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 --- include/linux/libnvdimm.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 0f0ab276134e..d0afa115356e 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -178,6 +178,8 @@ struct nvdimm_security_ops { const struct nvdimm_key_data *new_data); int (*unlock)(struct nvdimm *nvdimm, const struct nvdimm_key_data *key_data); + int (*disable)(struct nvdimm *nvdimm, + const struct nvdimm_key_data *key_data); }; void badrange_init(struct badrange *badrange); -- cgit v1.2.1 From 64e77c8c047fb91ea8c7800c1238108a72f0bf9c Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 7 Dec 2018 14:02:12 -0700 Subject: acpi/nfit, libnvdimm: Add support for issue secure erase DSM to Intel nvdimm Add support to issue a secure erase DSM to the Intel nvdimm. The required passphrase is acquired from an encrypted key in the kernel user keyring. To trigger the action, "erase " is written to the "security" sysfs attribute. Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- include/linux/libnvdimm.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index d0afa115356e..9a6cb7067dc7 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -180,6 +180,8 @@ struct nvdimm_security_ops { const struct nvdimm_key_data *key_data); int (*disable)(struct nvdimm *nvdimm, const struct nvdimm_key_data *key_data); + int (*erase)(struct nvdimm *nvdimm, + const struct nvdimm_key_data *key_data); }; void badrange_init(struct badrange *badrange); -- cgit v1.2.1 From 7d988097c546187ada602cc9bccd0f03d473eb8f Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 13 Dec 2018 15:36:18 -0700 Subject: acpi/nfit, libnvdimm/security: Add security DSM overwrite support Add support for the NVDIMM_FAMILY_INTEL "ovewrite" capability as described by the Intel DSM spec v1.7. This will allow triggering of overwrite on Intel NVDIMMs. The overwrite operation can take tens of minutes. When the overwrite DSM is issued successfully, the NVDIMMs will be unaccessible. The kernel will do backoff polling to detect when the overwrite process is completed. According to the DSM spec v1.7, the 128G NVDIMMs can take up to 15mins to perform overwrite and larger DIMMs will take longer. Given that overwrite puts the DIMM in an indeterminate state until it completes introduce the NDD_SECURITY_OVERWRITE flag to prevent other operations from executing when overwrite is happening. The NDD_WORK_PENDING flag is added to denote that there is a device reference on the nvdimm device for an async workqueue thread context. Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- include/linux/libnvdimm.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 9a6cb7067dc7..d18885304020 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -38,6 +38,10 @@ enum { NDD_UNARMED = 1, /* locked memory devices should not be accessed */ NDD_LOCKED = 2, + /* memory under security wipes should not be accessed */ + NDD_SECURITY_OVERWRITE = 3, + /* tracking whether or not there is a pending device reference */ + NDD_WORK_PENDING = 4, /* need to set a limit somewhere, but yes, this is likely overkill */ ND_IOCTL_MAX_BUFLEN = SZ_4M, @@ -182,6 +186,9 @@ struct nvdimm_security_ops { const struct nvdimm_key_data *key_data); int (*erase)(struct nvdimm *nvdimm, const struct nvdimm_key_data *key_data); + int (*overwrite)(struct nvdimm *nvdimm, + const struct nvdimm_key_data *key_data); + int (*query_overwrite)(struct nvdimm *nvdimm); }; void badrange_init(struct badrange *badrange); @@ -219,6 +226,7 @@ static inline struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, cmd_mask, num_flush, flush_wpq, NULL, NULL); } +int nvdimm_security_setup_events(struct nvdimm *nvdimm); 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, @@ -244,6 +252,7 @@ u64 nd_fletcher64(void *addr, size_t len, bool le); 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); +int nvdimm_in_overwrite(struct nvdimm *nvdimm); static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc) -- cgit v1.2.1 From 89fa9d8ea7bdfa841d19044485cec5f4171069e5 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Mon, 10 Dec 2018 10:53:22 -0700 Subject: acpi/nfit, libnvdimm/security: add Intel DSM 1.8 master passphrase support With Intel DSM 1.8 [1] two new security DSMs are introduced. Enable/update master passphrase and master secure erase. The master passphrase allows a secure erase to be performed without the user passphrase that is set on the NVDIMM. The commands of master_update and master_erase are added to the sysfs knob in order to initiate the DSMs. They are similar in opeartion mechanism compare to update and erase. [1]: http://pmem.io/documents/NVDIMM_DSM_Interface-V1.8.pdf Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- include/linux/libnvdimm.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index d18885304020..5440f11b0907 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -174,18 +174,26 @@ struct nvdimm_key_data { u8 data[NVDIMM_PASSPHRASE_LEN]; }; +enum nvdimm_passphrase_type { + NVDIMM_USER, + NVDIMM_MASTER, +}; + struct nvdimm_security_ops { - enum nvdimm_security_state (*state)(struct nvdimm *nvdimm); + enum nvdimm_security_state (*state)(struct nvdimm *nvdimm, + enum nvdimm_passphrase_type pass_type); 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); + const struct nvdimm_key_data *new_data, + enum nvdimm_passphrase_type pass_type); int (*unlock)(struct nvdimm *nvdimm, const struct nvdimm_key_data *key_data); int (*disable)(struct nvdimm *nvdimm, const struct nvdimm_key_data *key_data); int (*erase)(struct nvdimm *nvdimm, - const struct nvdimm_key_data *key_data); + const struct nvdimm_key_data *key_data, + enum nvdimm_passphrase_type pass_type); int (*overwrite)(struct nvdimm *nvdimm, const struct nvdimm_key_data *key_data); int (*query_overwrite)(struct nvdimm *nvdimm); -- cgit v1.2.1