diff options
Diffstat (limited to 'drivers/nvdimm/security.c')
-rw-r--r-- | drivers/nvdimm/security.c | 199 |
1 files changed, 133 insertions, 66 deletions
diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c index a570f2263a42..9e45b207ff01 100644 --- a/drivers/nvdimm/security.c +++ b/drivers/nvdimm/security.c @@ -158,7 +158,7 @@ static int nvdimm_key_revalidate(struct nvdimm *nvdimm) } nvdimm_put_key(key); - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); return 0; } @@ -174,7 +174,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm) lockdep_assert_held(&nvdimm_bus->reconfig_mutex); if (!nvdimm->sec.ops || !nvdimm->sec.ops->unlock - || nvdimm->sec.state < 0) + || !nvdimm->sec.flags) return -EIO; if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { @@ -189,7 +189,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm) * 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 (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) { if (!key_revalidate) return 0; @@ -202,7 +202,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm) rc == 0 ? "success" : "fail"); nvdimm_put_key(key); - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); return rc; } @@ -217,7 +217,25 @@ int nvdimm_security_unlock(struct device *dev) return rc; } -int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid) +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); @@ -229,19 +247,12 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid) lockdep_assert_held(&nvdimm_bus->reconfig_mutex); if (!nvdimm->sec.ops || !nvdimm->sec.ops->disable - || nvdimm->sec.state < 0) + || !nvdimm->sec.flags) return -EOPNOTSUPP; - if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) { - dev_dbg(dev, "Incorrect security state: %d\n", - nvdimm->sec.state); - return -EIO; - } - - if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { - dev_dbg(dev, "Security operation in progress.\n"); - return -EBUSY; - } + rc = check_security_state(nvdimm); + if (rc) + return rc; data = nvdimm_get_user_key_payload(nvdimm, keyid, NVDIMM_BASE_KEY, &key); @@ -253,11 +264,11 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid) rc == 0 ? "success" : "fail"); nvdimm_put_key(key); - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); return rc; } -int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid, +static int security_update(struct nvdimm *nvdimm, unsigned int keyid, unsigned int new_keyid, enum nvdimm_passphrase_type pass_type) { @@ -271,14 +282,12 @@ int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid, lockdep_assert_held(&nvdimm_bus->reconfig_mutex); if (!nvdimm->sec.ops || !nvdimm->sec.ops->change_key - || nvdimm->sec.state < 0) + || !nvdimm->sec.flags) return -EOPNOTSUPP; - if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) { - dev_dbg(dev, "Incorrect security state: %d\n", - nvdimm->sec.state); - return -EIO; - } + rc = check_security_state(nvdimm); + if (rc) + return rc; data = nvdimm_get_user_key_payload(nvdimm, keyid, NVDIMM_BASE_KEY, &key); @@ -301,15 +310,15 @@ int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid, nvdimm_put_key(newkey); nvdimm_put_key(key); if (pass_type == NVDIMM_MASTER) - nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, + nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER); else - nvdimm->sec.state = nvdimm_security_state(nvdimm, + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); return rc; } -int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid, +static int security_erase(struct nvdimm *nvdimm, unsigned int keyid, enum nvdimm_passphrase_type pass_type) { struct device *dev = &nvdimm->dev; @@ -322,26 +331,14 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid, lockdep_assert_held(&nvdimm_bus->reconfig_mutex); if (!nvdimm->sec.ops || !nvdimm->sec.ops->erase - || nvdimm->sec.state < 0) + || !nvdimm->sec.flags) return -EOPNOTSUPP; - if (atomic_read(&nvdimm->busy)) { - dev_dbg(dev, "Unable to secure erase while DIMM active.\n"); - return -EBUSY; - } - - if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) { - dev_dbg(dev, "Incorrect security state: %d\n", - nvdimm->sec.state); - return -EIO; - } - - if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { - dev_dbg(dev, "Security operation in progress.\n"); - return -EBUSY; - } + rc = check_security_state(nvdimm); + if (rc) + return rc; - if (nvdimm->sec.ext_state != NVDIMM_SECURITY_UNLOCKED + 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"); @@ -359,11 +356,11 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid, rc == 0 ? "success" : "fail"); nvdimm_put_key(key); - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); return rc; } -int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid) +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); @@ -375,29 +372,17 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid) lockdep_assert_held(&nvdimm_bus->reconfig_mutex); if (!nvdimm->sec.ops || !nvdimm->sec.ops->overwrite - || nvdimm->sec.state < 0) + || !nvdimm->sec.flags) return -EOPNOTSUPP; - if (atomic_read(&nvdimm->busy)) { - dev_dbg(dev, "Unable to overwrite while DIMM active.\n"); - return -EBUSY; - } - if (dev->driver == NULL) { dev_dbg(dev, "Unable to overwrite while DIMM active.\n"); return -EINVAL; } - if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) { - dev_dbg(dev, "Incorrect security state: %d\n", - nvdimm->sec.state); - return -EIO; - } - - if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { - dev_dbg(dev, "Security operation in progress.\n"); - return -EBUSY; - } + rc = check_security_state(nvdimm); + if (rc) + return rc; data = nvdimm_get_user_key_payload(nvdimm, keyid, NVDIMM_BASE_KEY, &key); @@ -412,7 +397,7 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid) if (rc == 0) { set_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags); set_bit(NDD_WORK_PENDING, &nvdimm->flags); - nvdimm->sec.state = NVDIMM_SECURITY_OVERWRITE; + set_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags); /* * Make sure we don't lose device while doing overwrite * query. @@ -443,7 +428,7 @@ void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm) tmo = nvdimm->sec.overwrite_tmo; if (!nvdimm->sec.ops || !nvdimm->sec.ops->query_overwrite - || nvdimm->sec.state < 0) + || !nvdimm->sec.flags) return; rc = nvdimm->sec.ops->query_overwrite(nvdimm); @@ -467,8 +452,8 @@ void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm) clear_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags); clear_bit(NDD_WORK_PENDING, &nvdimm->flags); put_device(&nvdimm->dev); - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); - nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER); } void nvdimm_security_overwrite_query(struct work_struct *work) @@ -480,3 +465,85 @@ void nvdimm_security_overwrite_query(struct work_struct *work) __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; +} |