summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Jiang <dave.jiang@intel.com>2018-12-06 15:40:01 -0500
committerDan Williams <dan.j.williams@intel.com>2018-12-13 20:54:13 -0500
commit4c6926a23b76ea23403976290cd45a7a143f6500 (patch)
tree55dc830eafb03c36a4445f6b03c7e5dbe46be2dd
parent37833fb7989a9d3c3e26354e6878e682c340d718 (diff)
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 <dave.jiang@intel.com> Co-developed-by: Dan Williams <dan.j.williams@intel.com> Reported-by: Randy Dunlap <rdunlap@infradead.org> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/acpi/nfit/intel.c109
-rw-r--r--drivers/nvdimm/Kconfig5
-rw-r--r--drivers/nvdimm/Makefile1
-rw-r--r--drivers/nvdimm/dimm.c16
-rw-r--r--drivers/nvdimm/nd.h8
-rw-r--r--drivers/nvdimm/security.c148
-rw-r--r--include/linux/libnvdimm.h12
-rw-r--r--tools/testing/nvdimm/Kbuild1
8 files changed, 299 insertions, 1 deletions
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 @@
3#include <linux/libnvdimm.h> 3#include <linux/libnvdimm.h>
4#include <linux/ndctl.h> 4#include <linux/ndctl.h>
5#include <linux/acpi.h> 5#include <linux/acpi.h>
6#include <asm/smp.h>
6#include "intel.h" 7#include "intel.h"
7#include "nfit.h" 8#include "nfit.h"
8 9
@@ -75,8 +76,116 @@ static int intel_security_freeze(struct nvdimm *nvdimm)
75 return 0; 76 return 0;
76} 77}
77 78
79static int intel_security_change_key(struct nvdimm *nvdimm,
80 const struct nvdimm_key_data *old_data,
81 const struct nvdimm_key_data *new_data)
82{
83 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
84 struct {
85 struct nd_cmd_pkg pkg;
86 struct nd_intel_set_passphrase cmd;
87 } nd_cmd = {
88 .pkg = {
89 .nd_command = NVDIMM_INTEL_SET_PASSPHRASE,
90 .nd_family = NVDIMM_FAMILY_INTEL,
91 .nd_size_in = ND_INTEL_PASSPHRASE_SIZE * 2,
92 .nd_size_out = ND_INTEL_STATUS_SIZE,
93 .nd_fw_size = ND_INTEL_STATUS_SIZE,
94 },
95 };
96 int rc;
97
98 if (!test_bit(NVDIMM_INTEL_SET_PASSPHRASE, &nfit_mem->dsm_mask))
99 return -ENOTTY;
100
101 if (old_data)
102 memcpy(nd_cmd.cmd.old_pass, old_data->data,
103 sizeof(nd_cmd.cmd.old_pass));
104 memcpy(nd_cmd.cmd.new_pass, new_data->data,
105 sizeof(nd_cmd.cmd.new_pass));
106 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
107 if (rc < 0)
108 return rc;
109
110 switch (nd_cmd.cmd.status) {
111 case 0:
112 return 0;
113 case ND_INTEL_STATUS_INVALID_PASS:
114 return -EINVAL;
115 case ND_INTEL_STATUS_NOT_SUPPORTED:
116 return -EOPNOTSUPP;
117 case ND_INTEL_STATUS_INVALID_STATE:
118 default:
119 return -EIO;
120 }
121}
122
123static void nvdimm_invalidate_cache(void);
124
125static int intel_security_unlock(struct nvdimm *nvdimm,
126 const struct nvdimm_key_data *key_data)
127{
128 struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
129 struct {
130 struct nd_cmd_pkg pkg;
131 struct nd_intel_unlock_unit cmd;
132 } nd_cmd = {
133 .pkg = {
134 .nd_command = NVDIMM_INTEL_UNLOCK_UNIT,
135 .nd_family = NVDIMM_FAMILY_INTEL,
136 .nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
137 .nd_size_out = ND_INTEL_STATUS_SIZE,
138 .nd_fw_size = ND_INTEL_STATUS_SIZE,
139 },
140 };
141 int rc;
142
143 if (!test_bit(NVDIMM_INTEL_UNLOCK_UNIT, &nfit_mem->dsm_mask))
144 return -ENOTTY;
145
146 memcpy(nd_cmd.cmd.passphrase, key_data->data,
147 sizeof(nd_cmd.cmd.passphrase));
148 rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
149 if (rc < 0)
150 return rc;
151 switch (nd_cmd.cmd.status) {
152 case 0:
153 break;
154 case ND_INTEL_STATUS_INVALID_PASS:
155 return -EINVAL;
156 default:
157 return -EIO;
158 }
159
160 /* DIMM unlocked, invalidate all CPU caches before we read it */
161 nvdimm_invalidate_cache();
162
163 return 0;
164}
165
166/*
167 * TODO: define a cross arch wbinvd equivalent when/if
168 * NVDIMM_FAMILY_INTEL command support arrives on another arch.
169 */
170#ifdef CONFIG_X86
171static void nvdimm_invalidate_cache(void)
172{
173 wbinvd_on_all_cpus();
174}
175#else
176static void nvdimm_invalidate_cache(void)
177{
178 WARN_ON_ONCE("cache invalidation required after unlock\n");
179}
180#endif
181
78static const struct nvdimm_security_ops __intel_security_ops = { 182static const struct nvdimm_security_ops __intel_security_ops = {
79 .state = intel_security_state, 183 .state = intel_security_state,
80 .freeze = intel_security_freeze, 184 .freeze = intel_security_freeze,
185 .change_key = intel_security_change_key,
186#ifdef CONFIG_X86
187 .unlock = intel_security_unlock,
188#endif
81}; 189};
190
82const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops; 191const 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
112 112
113 Select Y if unsure. 113 Select Y if unsure.
114 114
115config NVDIMM_KEYS
116 def_bool y
117 depends on ENCRYPTED_KEYS
118 depends on (LIBNVDIMM=ENCRYPTED_KEYS) || LIBNVDIMM=m
119
115endif 120endif
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
27libnvdimm-$(CONFIG_BTT) += btt_devs.o 27libnvdimm-$(CONFIG_BTT) += btt_devs.o
28libnvdimm-$(CONFIG_NVDIMM_PFN) += pfn_devs.o 28libnvdimm-$(CONFIG_NVDIMM_PFN) += pfn_devs.o
29libnvdimm-$(CONFIG_NVDIMM_DAX) += dax_devs.o 29libnvdimm-$(CONFIG_NVDIMM_DAX) += dax_devs.o
30libnvdimm-$(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)
34 return rc; 34 return rc;
35 } 35 }
36 36
37 /* reset locked, to be validated below... */ 37 /*
38 * The locked status bit reflects explicit status codes from the
39 * label reading commands, revalidate it each time the driver is
40 * activated and re-reads the label area.
41 */
38 nvdimm_clear_locked(dev); 42 nvdimm_clear_locked(dev);
39 43
40 ndd = kzalloc(sizeof(*ndd), GFP_KERNEL); 44 ndd = kzalloc(sizeof(*ndd), GFP_KERNEL);
@@ -52,6 +56,16 @@ static int nvdimm_probe(struct device *dev)
52 kref_init(&ndd->kref); 56 kref_init(&ndd->kref);
53 57
54 /* 58 /*
59 * Attempt to unlock, if the DIMM supports security commands,
60 * otherwise the locked indication is determined by explicit
61 * status codes from the label reading commands.
62 */
63 rc = nvdimm_security_unlock(dev);
64 if (rc < 0)
65 dev_err(dev, "failed to unlock dimm: %d\n", rc);
66
67
68 /*
55 * EACCES failures reading the namespace label-area-properties 69 * EACCES failures reading the namespace label-area-properties
56 * are interpreted as the DIMM capacity being locked but the 70 * are interpreted as the DIMM capacity being locked but the
57 * namespace labels themselves being accessible. 71 * namespace labels themselves being accessible.
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,
250void nvdimm_set_aliasing(struct device *dev); 250void nvdimm_set_aliasing(struct device *dev);
251void nvdimm_set_locked(struct device *dev); 251void nvdimm_set_locked(struct device *dev);
252void nvdimm_clear_locked(struct device *dev); 252void nvdimm_clear_locked(struct device *dev);
253#if IS_ENABLED(CONFIG_NVDIMM_KEYS)
254int nvdimm_security_unlock(struct device *dev);
255#else
256static inline int nvdimm_security_unlock(struct device *dev)
257{
258 return 0;
259}
260#endif
253struct nd_btt *to_nd_btt(struct device *dev); 261struct nd_btt *to_nd_btt(struct device *dev);
254 262
255struct nd_gen_sb { 263struct 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 @@
1// SPDX-License-Identifier: GPL-2.0
2/* Copyright(c) 2018 Intel Corporation. All rights reserved. */
3
4#include <linux/module.h>
5#include <linux/device.h>
6#include <linux/ndctl.h>
7#include <linux/slab.h>
8#include <linux/io.h>
9#include <linux/mm.h>
10#include <linux/cred.h>
11#include <linux/key.h>
12#include <linux/key-type.h>
13#include <keys/user-type.h>
14#include <keys/encrypted-type.h>
15#include "nd-core.h"
16#include "nd.h"
17
18static bool key_revalidate = true;
19module_param(key_revalidate, bool, 0444);
20MODULE_PARM_DESC(key_revalidate, "Require key validation at init.");
21
22static void *key_data(struct key *key)
23{
24 struct encrypted_key_payload *epayload = dereference_key_locked(key);
25
26 lockdep_assert_held_read(&key->sem);
27
28 return epayload->decrypted_data;
29}
30
31static void nvdimm_put_key(struct key *key)
32{
33 up_read(&key->sem);
34 key_put(key);
35}
36
37/*
38 * Retrieve kernel key for DIMM and request from user space if
39 * necessary. Returns a key held for read and must be put by
40 * nvdimm_put_key() before the usage goes out of scope.
41 */
42static struct key *nvdimm_request_key(struct nvdimm *nvdimm)
43{
44 struct key *key = NULL;
45 static const char NVDIMM_PREFIX[] = "nvdimm:";
46 char desc[NVDIMM_KEY_DESC_LEN + sizeof(NVDIMM_PREFIX)];
47 struct device *dev = &nvdimm->dev;
48
49 sprintf(desc, "%s%s", NVDIMM_PREFIX, nvdimm->dimm_id);
50 key = request_key(&key_type_encrypted, desc, "");
51 if (IS_ERR(key)) {
52 if (PTR_ERR(key) == -ENOKEY)
53 dev_warn(dev, "request_key() found no key\n");
54 else
55 dev_warn(dev, "request_key() upcall failed\n");
56 key = NULL;
57 } else {
58 struct encrypted_key_payload *epayload;
59
60 down_read(&key->sem);
61 epayload = dereference_key_locked(key);
62 if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) {
63 up_read(&key->sem);
64 key_put(key);
65 key = NULL;
66 }
67 }
68
69 return key;
70}
71
72static struct key *nvdimm_key_revalidate(struct nvdimm *nvdimm)
73{
74 struct key *key;
75 int rc;
76
77 if (!nvdimm->sec.ops->change_key)
78 return NULL;
79
80 key = nvdimm_request_key(nvdimm);
81 if (!key)
82 return NULL;
83
84 /*
85 * Send the same key to the hardware as new and old key to
86 * verify that the key is good.
87 */
88 rc = nvdimm->sec.ops->change_key(nvdimm, key_data(key), key_data(key));
89 if (rc < 0) {
90 nvdimm_put_key(key);
91 key = NULL;
92 }
93 return key;
94}
95
96static int __nvdimm_security_unlock(struct nvdimm *nvdimm)
97{
98 struct device *dev = &nvdimm->dev;
99 struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
100 struct key *key = NULL;
101 int rc;
102
103 /* The bus lock should be held at the top level of the call stack */
104 lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
105
106 if (!nvdimm->sec.ops || !nvdimm->sec.ops->unlock
107 || nvdimm->sec.state < 0)
108 return -EIO;
109
110 /*
111 * If the pre-OS has unlocked the DIMM, attempt to send the key
112 * from request_key() to the hardware for verification. Failure
113 * to revalidate the key against the hardware results in a
114 * freeze of the security configuration. I.e. if the OS does not
115 * have the key, security is being managed pre-OS.
116 */
117 if (nvdimm->sec.state == NVDIMM_SECURITY_UNLOCKED) {
118 if (!key_revalidate)
119 return 0;
120
121 key = nvdimm_key_revalidate(nvdimm);
122 if (!key)
123 return nvdimm_security_freeze(nvdimm);
124 } else
125 key = nvdimm_request_key(nvdimm);
126
127 if (!key)
128 return -ENOKEY;
129
130 rc = nvdimm->sec.ops->unlock(nvdimm, key_data(key));
131 dev_dbg(dev, "key: %d unlock: %s\n", key_serial(key),
132 rc == 0 ? "success" : "fail");
133
134 nvdimm_put_key(key);
135 nvdimm->sec.state = nvdimm_security_state(nvdimm);
136 return rc;
137}
138
139int nvdimm_security_unlock(struct device *dev)
140{
141 struct nvdimm *nvdimm = to_nvdimm(dev);
142 int rc;
143
144 nvdimm_bus_lock(dev);
145 rc = __nvdimm_security_unlock(nvdimm);
146 nvdimm_bus_unlock(dev);
147 return rc;
148}
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 {
163 NVDIMM_SECURITY_OVERWRITE, 163 NVDIMM_SECURITY_OVERWRITE,
164}; 164};
165 165
166#define NVDIMM_PASSPHRASE_LEN 32
167#define NVDIMM_KEY_DESC_LEN 22
168
169struct nvdimm_key_data {
170 u8 data[NVDIMM_PASSPHRASE_LEN];
171};
172
166struct nvdimm_security_ops { 173struct nvdimm_security_ops {
167 enum nvdimm_security_state (*state)(struct nvdimm *nvdimm); 174 enum nvdimm_security_state (*state)(struct nvdimm *nvdimm);
168 int (*freeze)(struct nvdimm *nvdimm); 175 int (*freeze)(struct nvdimm *nvdimm);
176 int (*change_key)(struct nvdimm *nvdimm,
177 const struct nvdimm_key_data *old_data,
178 const struct nvdimm_key_data *new_data);
179 int (*unlock)(struct nvdimm *nvdimm,
180 const struct nvdimm_key_data *key_data);
169}; 181};
170 182
171void badrange_init(struct badrange *badrange); 183void 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
80libnvdimm-$(CONFIG_BTT) += $(NVDIMM_SRC)/btt_devs.o 80libnvdimm-$(CONFIG_BTT) += $(NVDIMM_SRC)/btt_devs.o
81libnvdimm-$(CONFIG_NVDIMM_PFN) += $(NVDIMM_SRC)/pfn_devs.o 81libnvdimm-$(CONFIG_NVDIMM_PFN) += $(NVDIMM_SRC)/pfn_devs.o
82libnvdimm-$(CONFIG_NVDIMM_DAX) += $(NVDIMM_SRC)/dax_devs.o 82libnvdimm-$(CONFIG_NVDIMM_DAX) += $(NVDIMM_SRC)/dax_devs.o
83libnvdimm-$(CONFIG_NVDIMM_KEYS) += $(NVDIMM_SRC)/security.o
83libnvdimm-y += libnvdimm_test.o 84libnvdimm-y += libnvdimm_test.o
84libnvdimm-y += config_check.o 85libnvdimm-y += config_check.o
85 86