Skip to content

Commit

Permalink
Merge tag 'libnvdimm-for-4.21' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…kernel/git/nvdimm/nvdimm

Pull libnvdimm updates from Dan Williams:
 "The vast bulk of this update is the new support for the security
  capabilities of some nvdimms.

  The userspace tooling for this capability is still a work in progress,
  but the changes survive the existing libnvdimm unit tests. The changes
  also pass manual checkout on hardware and the new nfit_test emulation
  of the security capability.

  The touches of the security/keys/ files have received the necessary
  acks from Mimi and David. Those changes were necessary to allow for a
  new generic encrypted-key type, and allow the nvdimm sub-system to
  lookup key material referenced by the libnvdimm-sysfs interface.

  Summary:

   - Add support for the security features of nvdimm devices that
     implement a security model similar to ATA hard drive security. The
     security model supports locking access to the media at
     device-power-loss, to be unlocked with a passphrase, and
     secure-erase (crypto-scramble).

     Unlike the ATA security case where the kernel expects device
     security to be managed in a pre-OS environment, the libnvdimm
     security implementation allows key provisioning and key-operations
     at OS runtime. Keys are managed with the kernel's encrypted-keys
     facility to provide data-at-rest security for the libnvdimm key
     material. The usage model mirrors fscrypt key management, but is
     driven via libnvdimm sysfs.

   - Miscellaneous updates for api usage and comment fixes"

* tag 'libnvdimm-for-4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm: (21 commits)
  libnvdimm/security: Quiet security operations
  libnvdimm/security: Add documentation for nvdimm security support
  tools/testing/nvdimm: add Intel DSM 1.8 support for nfit_test
  tools/testing/nvdimm: Add overwrite support for nfit_test
  tools/testing/nvdimm: Add test support for Intel nvdimm security DSMs
  acpi/nfit, libnvdimm/security: add Intel DSM 1.8 master passphrase support
  acpi/nfit, libnvdimm/security: Add security DSM overwrite support
  acpi/nfit, libnvdimm: Add support for issue secure erase DSM to Intel nvdimm
  acpi/nfit, libnvdimm: Add enable/update passphrase support for Intel nvdimms
  acpi/nfit, libnvdimm: Add disable passphrase support to Intel nvdimm.
  acpi/nfit, libnvdimm: Add unlock of nvdimm support for Intel DIMMs
  acpi/nfit, libnvdimm: Add freeze security support to Intel nvdimm
  acpi/nfit, libnvdimm: Introduce nvdimm_security_ops
  keys-encrypted: add nvdimm key format type to encrypted keys
  keys: Export lookup_user_key to external users
  acpi/nfit, libnvdimm: Store dimm id as a member to struct nvdimm
  libnvdimm, namespace: Replace kmemdup() with kstrndup()
  libnvdimm, label: Switch to bitmap_zalloc()
  ACPI/nfit: Adjust annotation for why return 0 if fail to find NFIT at start
  libnvdimm, bus: Check id immediately following ida_simple_get
  ...
  • Loading branch information
torvalds committed Dec 28, 2018
2 parents 4ed7bdc + 4b5f747 commit 75f95da
Show file tree
Hide file tree
Showing 27 changed files with 1,971 additions and 54 deletions.
141 changes: 141 additions & 0 deletions Documentation/nvdimm/security.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
NVDIMM SECURITY
===============

1. Introduction
---------------

With the introduction of Intel Device Specific Methods (DSM) v1.8
specification [1], security DSMs are introduced. The spec added the following
security DSMs: "get security state", "set passphrase", "disable passphrase",
"unlock unit", "freeze lock", "secure erase", and "overwrite". A security_ops
data structure has been added to struct dimm in order to support the security
operations and generic APIs are exposed to allow vendor neutral operations.

2. Sysfs Interface
------------------
The "security" sysfs attribute is provided in the nvdimm sysfs directory. For
example:
/sys/devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0012:00/ndbus0/nmem0/security

The "show" attribute of that attribute will display the security state for
that DIMM. The following states are available: disabled, unlocked, locked,
frozen, and overwrite. If security is not supported, the sysfs attribute
will not be visible.

The "store" attribute takes several commands when it is being written to
in order to support some of the security functionalities:
update <old_keyid> <new_keyid> - enable or update passphrase.
disable <keyid> - disable enabled security and remove key.
freeze - freeze changing of security states.
erase <keyid> - delete existing user encryption key.
overwrite <keyid> - wipe the entire nvdimm.
master_update <keyid> <new_keyid> - enable or update master passphrase.
master_erase <keyid> - delete existing user encryption key.

3. Key Management
-----------------

The key is associated to the payload by the DIMM id. For example:
# cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0012:00/ndbus0/nmem0/nfit/id
8089-a2-1740-00000133
The DIMM id would be provided along with the key payload (passphrase) to
the kernel.

The security keys are managed on the basis of a single key per DIMM. The
key "passphrase" is expected to be 32bytes long. This is similar to the ATA
security specification [2]. A key is initially acquired via the request_key()
kernel API call during nvdimm unlock. It is up to the user to make sure that
all the keys are in the kernel user keyring for unlock.

A nvdimm encrypted-key of format enc32 has the description format of:
nvdimm:<bus-provider-specific-unique-id>

See file ``Documentation/security/keys/trusted-encrypted.rst`` for creating
encrypted-keys of enc32 format. TPM usage with a master trusted key is
preferred for sealing the encrypted-keys.

4. Unlocking
------------
When the DIMMs are being enumerated by the kernel, the kernel will attempt to
retrieve the key from the kernel user keyring. This is the only time
a locked DIMM can be unlocked. Once unlocked, the DIMM will remain unlocked
until reboot. Typically an entity (i.e. shell script) will inject all the
relevant encrypted-keys into the kernel user keyring during the initramfs phase.
This provides the unlock function access to all the related keys that contain
the passphrase for the respective nvdimms. It is also recommended that the
keys are injected before libnvdimm is loaded by modprobe.

5. Update
---------
When doing an update, it is expected that the existing key is removed from
the kernel user keyring and reinjected as different (old) key. It's irrelevant
what the key description is for the old key since we are only interested in the
keyid when doing the update operation. It is also expected that the new key
is injected with the description format described from earlier in this
document. The update command written to the sysfs attribute will be with
the format:
update <old keyid> <new keyid>

If there is no old keyid due to a security enabling, then a 0 should be
passed in.

6. Freeze
---------
The freeze operation does not require any keys. The security config can be
frozen by a user with root privelege.

7. Disable
----------
The security disable command format is:
disable <keyid>

An key with the current passphrase payload that is tied to the nvdimm should be
in the kernel user keyring.

8. Secure Erase
---------------
The command format for doing a secure erase is:
erase <keyid>

An key with the current passphrase payload that is tied to the nvdimm should be
in the kernel user keyring.

9. Overwrite
------------
The command format for doing an overwrite is:
overwrite <keyid>

Overwrite can be done without a key if security is not enabled. A key serial
of 0 can be passed in to indicate no key.

The sysfs attribute "security" can be polled to wait on overwrite completion.
Overwrite can last tens of minutes or more depending on nvdimm size.

An encrypted-key with the current user passphrase that is tied to the nvdimm
should be injected and its keyid should be passed in via sysfs.

10. Master Update
-----------------
The command format for doing a master update is:
update <old keyid> <new keyid>

The operating mechanism for master update is identical to update except the
master passphrase key is passed to the kernel. The master passphrase key
is just another encrypted-key.

This command is only available when security is disabled.

11. Master Erase
----------------
The command format for doing a master erase is:
master_erase <current keyid>

This command has the same operating mechanism as erase except the master
passphrase key is passed to the kernel. The master passphrase key is just
another encrypted-key.

This command is only available when the master security is enabled, indicated
by the extended security status.

[1]: http://pmem.io/documents/NVDIMM_DSM_Interface-V1.8.pdf
[2]: http://www.t13.org/documents/UploadedDocuments/docs2006/e05179r4-ACS-SecurityClarifications.pdf
6 changes: 5 additions & 1 deletion Documentation/security/keys/trusted-encrypted.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Usage::

Where::

format:= 'default | ecryptfs'
format:= 'default | ecryptfs | enc32'
key-type:= 'trusted' | 'user'


Expand Down Expand Up @@ -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.
11 changes: 11 additions & 0 deletions drivers/acpi/nfit/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions drivers/acpi/nfit/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
obj-$(CONFIG_ACPI_NFIT) := nfit.o
nfit-y := core.o
nfit-y += intel.o
nfit-$(CONFIG_X86_MCE) += mce.o
103 changes: 85 additions & 18 deletions drivers/acpi/nfit/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/nd.h>
#include <asm/cacheflush.h>
#include <acpi/nfit.h>
#include "intel.h"
#include "nfit.h"
#include "intel.h"

Expand Down Expand Up @@ -380,6 +381,16 @@ 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,
[NVDIMM_INTEL_SET_MASTER_PASSPHRASE] = 2,
[NVDIMM_INTEL_MASTER_SECURE_ERASE] = 2,
},
};
u8 id;
Expand All @@ -394,6 +405,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)
{
Expand Down Expand Up @@ -478,9 +500,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
Expand Down Expand Up @@ -1573,18 +1596,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);

Expand Down Expand Up @@ -1780,10 +1795,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 */
Expand Down Expand Up @@ -1904,6 +1932,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;
Expand Down Expand Up @@ -1970,10 +2008,11 @@ 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],
acpi_nfit_get_security_ops(nfit_mem->family));
if (!nvdimm)
return -ENOMEM;

Expand Down Expand Up @@ -2008,6 +2047,11 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
if (!nvdimm)
continue;

rc = nvdimm_security_setup_events(nvdimm);
if (rc < 0)
dev_warn(acpi_desc->dev,
"security event setup failed: %d\n", rc);

nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit");
if (nfit_kernfs)
nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs,
Expand Down Expand Up @@ -3337,7 +3381,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);
Expand All @@ -3359,6 +3403,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)
{
Expand Down Expand Up @@ -3474,7 +3535,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;
}
Expand Down
Loading

0 comments on commit 75f95da

Please sign in to comment.