123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright(c) 2018 Intel Corporation. All rights reserved. */
- #include <linux/module.h>
- #include <linux/device.h>
- #include <linux/ndctl.h>
- #include <linux/slab.h>
- #include <linux/io.h>
- #include <linux/mm.h>
- #include <linux/cred.h>
- #include <linux/key.h>
- #include <linux/key-type.h>
- #include <keys/user-type.h>
- #include <keys/encrypted-type.h>
- #include "nd-core.h"
- #include "nd.h"
- #define NVDIMM_BASE_KEY 0
- #define NVDIMM_NEW_KEY 1
- static bool key_revalidate = true;
- module_param(key_revalidate, bool, 0444);
- MODULE_PARM_DESC(key_revalidate, "Require key validation at init.");
- static const char zero_key[NVDIMM_PASSPHRASE_LEN];
- 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)
- {
- if (!key)
- return;
- 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_dbg(dev, "request_key() found no key\n");
- else
- dev_dbg(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 const void *nvdimm_get_key_payload(struct nvdimm *nvdimm,
- struct key **key)
- {
- *key = nvdimm_request_key(nvdimm);
- if (!*key)
- return zero_key;
- return key_data(*key);
- }
- static struct key *nvdimm_lookup_user_key(struct nvdimm *nvdimm,
- key_serial_t id, int subclass)
- {
- key_ref_t keyref;
- struct key *key;
- struct encrypted_key_payload *epayload;
- struct device *dev = &nvdimm->dev;
- keyref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
- 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_nested(&key->sem, subclass);
- epayload = dereference_key_locked(key);
- if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) {
- up_read(&key->sem);
- key_put(key);
- key = NULL;
- }
- return key;
- }
- static const void *nvdimm_get_user_key_payload(struct nvdimm *nvdimm,
- key_serial_t id, int subclass, struct key **key)
- {
- *key = NULL;
- if (id == 0) {
- if (subclass == NVDIMM_BASE_KEY)
- return zero_key;
- else
- return NULL;
- }
- *key = nvdimm_lookup_user_key(nvdimm, id, subclass);
- if (!*key)
- return NULL;
- return key_data(*key);
- }
- static int nvdimm_key_revalidate(struct nvdimm *nvdimm)
- {
- struct key *key;
- int rc;
- const void *data;
- if (!nvdimm->sec.ops->change_key)
- return -EOPNOTSUPP;
- data = nvdimm_get_key_payload(nvdimm, &key);
- /*
- * 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, data, data, NVDIMM_USER);
- if (rc < 0) {
- nvdimm_put_key(key);
- return rc;
- }
- nvdimm_put_key(key);
- nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
- return 0;
- }
- 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;
- const void *data;
- 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.flags)
- return -EIO;
- /* No need to go further if security is disabled */
- if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
- return 0;
- if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
- dev_dbg(dev, "Security operation in progress.\n");
- return -EBUSY;
- }
- /*
- * 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 (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) {
- if (!key_revalidate)
- return 0;
- return nvdimm_key_revalidate(nvdimm);
- } else
- data = nvdimm_get_key_payload(nvdimm, &key);
- rc = nvdimm->sec.ops->unlock(nvdimm, data);
- dev_dbg(dev, "key: %d unlock: %s\n", key_serial(key),
- rc == 0 ? "success" : "fail");
- nvdimm_put_key(key);
- nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
- 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;
- }
- static int check_security_state(struct nvdimm *nvdimm)
- {
- struct device *dev = &nvdimm->dev;
- if (test_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags)) {
- dev_dbg(dev, "Incorrect security state: %#lx\n",
- nvdimm->sec.flags);
- return -EIO;
- }
- if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
- dev_dbg(dev, "Security operation in progress.\n");
- return -EBUSY;
- }
- return 0;
- }
- static int security_disable(struct nvdimm *nvdimm, unsigned int keyid)
- {
- struct device *dev = &nvdimm->dev;
- struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
- struct key *key;
- int rc;
- const void *data;
- /* 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->disable
- || !nvdimm->sec.flags)
- return -EOPNOTSUPP;
- rc = check_security_state(nvdimm);
- if (rc)
- return rc;
- data = nvdimm_get_user_key_payload(nvdimm, keyid,
- NVDIMM_BASE_KEY, &key);
- if (!data)
- return -ENOKEY;
- rc = nvdimm->sec.ops->disable(nvdimm, data);
- dev_dbg(dev, "key: %d disable: %s\n", key_serial(key),
- rc == 0 ? "success" : "fail");
- nvdimm_put_key(key);
- nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
- return rc;
- }
- static int security_update(struct nvdimm *nvdimm, unsigned int keyid,
- unsigned int new_keyid,
- enum nvdimm_passphrase_type pass_type)
- {
- struct device *dev = &nvdimm->dev;
- struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
- struct key *key, *newkey;
- int rc;
- const void *data, *newdata;
- /* 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->change_key
- || !nvdimm->sec.flags)
- return -EOPNOTSUPP;
- rc = check_security_state(nvdimm);
- if (rc)
- return rc;
- data = nvdimm_get_user_key_payload(nvdimm, keyid,
- NVDIMM_BASE_KEY, &key);
- if (!data)
- return -ENOKEY;
- newdata = nvdimm_get_user_key_payload(nvdimm, new_keyid,
- NVDIMM_NEW_KEY, &newkey);
- if (!newdata) {
- nvdimm_put_key(key);
- return -ENOKEY;
- }
- rc = nvdimm->sec.ops->change_key(nvdimm, data, newdata, pass_type);
- dev_dbg(dev, "key: %d %d update%s: %s\n",
- key_serial(key), key_serial(newkey),
- pass_type == NVDIMM_MASTER ? "(master)" : "(user)",
- rc == 0 ? "success" : "fail");
- nvdimm_put_key(newkey);
- nvdimm_put_key(key);
- if (pass_type == NVDIMM_MASTER)
- nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm,
- NVDIMM_MASTER);
- else
- nvdimm->sec.flags = nvdimm_security_flags(nvdimm,
- NVDIMM_USER);
- return rc;
- }
- static int security_erase(struct nvdimm *nvdimm, unsigned int keyid,
- enum nvdimm_passphrase_type pass_type)
- {
- struct device *dev = &nvdimm->dev;
- struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
- struct key *key = NULL;
- int rc;
- const void *data;
- /* 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->erase
- || !nvdimm->sec.flags)
- return -EOPNOTSUPP;
- rc = check_security_state(nvdimm);
- if (rc)
- return rc;
- if (!test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.ext_flags)
- && pass_type == NVDIMM_MASTER) {
- dev_dbg(dev,
- "Attempt to secure erase in wrong master state.\n");
- return -EOPNOTSUPP;
- }
- data = nvdimm_get_user_key_payload(nvdimm, keyid,
- NVDIMM_BASE_KEY, &key);
- if (!data)
- return -ENOKEY;
- rc = nvdimm->sec.ops->erase(nvdimm, data, pass_type);
- dev_dbg(dev, "key: %d erase%s: %s\n", key_serial(key),
- pass_type == NVDIMM_MASTER ? "(master)" : "(user)",
- rc == 0 ? "success" : "fail");
- nvdimm_put_key(key);
- nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
- return rc;
- }
- static int security_overwrite(struct nvdimm *nvdimm, unsigned int keyid)
- {
- struct device *dev = &nvdimm->dev;
- struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
- struct key *key = NULL;
- int rc;
- const void *data;
- /* 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->overwrite
- || !nvdimm->sec.flags)
- return -EOPNOTSUPP;
- rc = check_security_state(nvdimm);
- if (rc)
- return rc;
- data = nvdimm_get_user_key_payload(nvdimm, keyid,
- NVDIMM_BASE_KEY, &key);
- if (!data)
- return -ENOKEY;
- rc = nvdimm->sec.ops->overwrite(nvdimm, data);
- dev_dbg(dev, "key: %d overwrite submission: %s\n", key_serial(key),
- rc == 0 ? "success" : "fail");
- nvdimm_put_key(key);
- if (rc == 0) {
- set_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
- set_bit(NDD_WORK_PENDING, &nvdimm->flags);
- set_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags);
- /*
- * Make sure we don't lose device while doing overwrite
- * query.
- */
- get_device(dev);
- queue_delayed_work(system_wq, &nvdimm->dwork, 0);
- }
- return rc;
- }
- static void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm)
- {
- struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nvdimm->dev);
- int rc;
- unsigned int tmo;
- /* The bus lock should be held at the top level of the call stack */
- lockdep_assert_held(&nvdimm_bus->reconfig_mutex);
- /*
- * Abort and release device if we no longer have the overwrite
- * flag set. It means the work has been canceled.
- */
- if (!test_bit(NDD_WORK_PENDING, &nvdimm->flags))
- return;
- tmo = nvdimm->sec.overwrite_tmo;
- if (!nvdimm->sec.ops || !nvdimm->sec.ops->query_overwrite
- || !nvdimm->sec.flags)
- return;
- rc = nvdimm->sec.ops->query_overwrite(nvdimm);
- if (rc == -EBUSY) {
- /* setup delayed work again */
- tmo += 10;
- queue_delayed_work(system_wq, &nvdimm->dwork, tmo * HZ);
- nvdimm->sec.overwrite_tmo = min(15U * 60U, tmo);
- return;
- }
- if (rc < 0)
- dev_dbg(&nvdimm->dev, "overwrite failed\n");
- else
- dev_dbg(&nvdimm->dev, "overwrite completed\n");
- /*
- * Mark the overwrite work done and update dimm security flags,
- * then send a sysfs event notification to wake up userspace
- * poll threads to picked up the changed state.
- */
- nvdimm->sec.overwrite_tmo = 0;
- clear_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
- clear_bit(NDD_WORK_PENDING, &nvdimm->flags);
- nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
- nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
- if (nvdimm->sec.overwrite_state)
- sysfs_notify_dirent(nvdimm->sec.overwrite_state);
- put_device(&nvdimm->dev);
- }
- void nvdimm_security_overwrite_query(struct work_struct *work)
- {
- struct nvdimm *nvdimm =
- container_of(work, typeof(*nvdimm), dwork.work);
- nvdimm_bus_lock(&nvdimm->dev);
- __nvdimm_security_overwrite_query(nvdimm);
- nvdimm_bus_unlock(&nvdimm->dev);
- }
- #define OPS \
- C( OP_FREEZE, "freeze", 1), \
- C( OP_DISABLE, "disable", 2), \
- C( OP_UPDATE, "update", 3), \
- C( OP_ERASE, "erase", 2), \
- C( OP_OVERWRITE, "overwrite", 2), \
- C( OP_MASTER_UPDATE, "master_update", 3), \
- C( OP_MASTER_ERASE, "master_erase", 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
- ssize_t nvdimm_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;
- 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 = security_disable(nvdimm, key);
- } else if (i == OP_UPDATE || i == OP_MASTER_UPDATE) {
- dev_dbg(dev, "%s %u %u\n", ops[i].name, key, newkey);
- rc = security_update(nvdimm, key, newkey, i == OP_UPDATE
- ? NVDIMM_USER : NVDIMM_MASTER);
- } else if (i == OP_ERASE || i == OP_MASTER_ERASE) {
- dev_dbg(dev, "%s %u\n", ops[i].name, key);
- if (atomic_read(&nvdimm->busy)) {
- dev_dbg(dev, "Unable to secure erase while DIMM active.\n");
- return -EBUSY;
- }
- rc = security_erase(nvdimm, key, i == OP_ERASE
- ? NVDIMM_USER : NVDIMM_MASTER);
- } else if (i == OP_OVERWRITE) {
- dev_dbg(dev, "overwrite %u\n", key);
- if (atomic_read(&nvdimm->busy)) {
- dev_dbg(dev, "Unable to overwrite while DIMM active.\n");
- return -EBUSY;
- }
- rc = security_overwrite(nvdimm, key);
- } else
- return -EINVAL;
- if (rc == 0)
- rc = len;
- return rc;
- }
|