18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright(c) 2018 Intel Corporation. All rights reserved. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/module.h> 58c2ecf20Sopenharmony_ci#include <linux/device.h> 68c2ecf20Sopenharmony_ci#include <linux/ndctl.h> 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci#include <linux/io.h> 98c2ecf20Sopenharmony_ci#include <linux/mm.h> 108c2ecf20Sopenharmony_ci#include <linux/cred.h> 118c2ecf20Sopenharmony_ci#include <linux/key.h> 128c2ecf20Sopenharmony_ci#include <linux/key-type.h> 138c2ecf20Sopenharmony_ci#include <keys/user-type.h> 148c2ecf20Sopenharmony_ci#include <keys/encrypted-type.h> 158c2ecf20Sopenharmony_ci#include "nd-core.h" 168c2ecf20Sopenharmony_ci#include "nd.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define NVDIMM_BASE_KEY 0 198c2ecf20Sopenharmony_ci#define NVDIMM_NEW_KEY 1 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic bool key_revalidate = true; 228c2ecf20Sopenharmony_cimodule_param(key_revalidate, bool, 0444); 238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(key_revalidate, "Require key validation at init."); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic const char zero_key[NVDIMM_PASSPHRASE_LEN]; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void *key_data(struct key *key) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct encrypted_key_payload *epayload = dereference_key_locked(key); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci lockdep_assert_held_read(&key->sem); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci return epayload->decrypted_data; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void nvdimm_put_key(struct key *key) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci if (!key) 398c2ecf20Sopenharmony_ci return; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci up_read(&key->sem); 428c2ecf20Sopenharmony_ci key_put(key); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * Retrieve kernel key for DIMM and request from user space if 478c2ecf20Sopenharmony_ci * necessary. Returns a key held for read and must be put by 488c2ecf20Sopenharmony_ci * nvdimm_put_key() before the usage goes out of scope. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistatic struct key *nvdimm_request_key(struct nvdimm *nvdimm) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct key *key = NULL; 538c2ecf20Sopenharmony_ci static const char NVDIMM_PREFIX[] = "nvdimm:"; 548c2ecf20Sopenharmony_ci char desc[NVDIMM_KEY_DESC_LEN + sizeof(NVDIMM_PREFIX)]; 558c2ecf20Sopenharmony_ci struct device *dev = &nvdimm->dev; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci sprintf(desc, "%s%s", NVDIMM_PREFIX, nvdimm->dimm_id); 588c2ecf20Sopenharmony_ci key = request_key(&key_type_encrypted, desc, ""); 598c2ecf20Sopenharmony_ci if (IS_ERR(key)) { 608c2ecf20Sopenharmony_ci if (PTR_ERR(key) == -ENOKEY) 618c2ecf20Sopenharmony_ci dev_dbg(dev, "request_key() found no key\n"); 628c2ecf20Sopenharmony_ci else 638c2ecf20Sopenharmony_ci dev_dbg(dev, "request_key() upcall failed\n"); 648c2ecf20Sopenharmony_ci key = NULL; 658c2ecf20Sopenharmony_ci } else { 668c2ecf20Sopenharmony_ci struct encrypted_key_payload *epayload; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci down_read(&key->sem); 698c2ecf20Sopenharmony_ci epayload = dereference_key_locked(key); 708c2ecf20Sopenharmony_ci if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) { 718c2ecf20Sopenharmony_ci up_read(&key->sem); 728c2ecf20Sopenharmony_ci key_put(key); 738c2ecf20Sopenharmony_ci key = NULL; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return key; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic const void *nvdimm_get_key_payload(struct nvdimm *nvdimm, 818c2ecf20Sopenharmony_ci struct key **key) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci *key = nvdimm_request_key(nvdimm); 848c2ecf20Sopenharmony_ci if (!*key) 858c2ecf20Sopenharmony_ci return zero_key; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return key_data(*key); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic struct key *nvdimm_lookup_user_key(struct nvdimm *nvdimm, 918c2ecf20Sopenharmony_ci key_serial_t id, int subclass) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci key_ref_t keyref; 948c2ecf20Sopenharmony_ci struct key *key; 958c2ecf20Sopenharmony_ci struct encrypted_key_payload *epayload; 968c2ecf20Sopenharmony_ci struct device *dev = &nvdimm->dev; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci keyref = lookup_user_key(id, 0, KEY_NEED_SEARCH); 998c2ecf20Sopenharmony_ci if (IS_ERR(keyref)) 1008c2ecf20Sopenharmony_ci return NULL; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci key = key_ref_to_ptr(keyref); 1038c2ecf20Sopenharmony_ci if (key->type != &key_type_encrypted) { 1048c2ecf20Sopenharmony_ci key_put(key); 1058c2ecf20Sopenharmony_ci return NULL; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: key found: %#x\n", __func__, key_serial(key)); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci down_read_nested(&key->sem, subclass); 1118c2ecf20Sopenharmony_ci epayload = dereference_key_locked(key); 1128c2ecf20Sopenharmony_ci if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) { 1138c2ecf20Sopenharmony_ci up_read(&key->sem); 1148c2ecf20Sopenharmony_ci key_put(key); 1158c2ecf20Sopenharmony_ci key = NULL; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci return key; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic const void *nvdimm_get_user_key_payload(struct nvdimm *nvdimm, 1218c2ecf20Sopenharmony_ci key_serial_t id, int subclass, struct key **key) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci *key = NULL; 1248c2ecf20Sopenharmony_ci if (id == 0) { 1258c2ecf20Sopenharmony_ci if (subclass == NVDIMM_BASE_KEY) 1268c2ecf20Sopenharmony_ci return zero_key; 1278c2ecf20Sopenharmony_ci else 1288c2ecf20Sopenharmony_ci return NULL; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci *key = nvdimm_lookup_user_key(nvdimm, id, subclass); 1328c2ecf20Sopenharmony_ci if (!*key) 1338c2ecf20Sopenharmony_ci return NULL; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return key_data(*key); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int nvdimm_key_revalidate(struct nvdimm *nvdimm) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct key *key; 1428c2ecf20Sopenharmony_ci int rc; 1438c2ecf20Sopenharmony_ci const void *data; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (!nvdimm->sec.ops->change_key) 1468c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci data = nvdimm_get_key_payload(nvdimm, &key); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* 1518c2ecf20Sopenharmony_ci * Send the same key to the hardware as new and old key to 1528c2ecf20Sopenharmony_ci * verify that the key is good. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci rc = nvdimm->sec.ops->change_key(nvdimm, data, data, NVDIMM_USER); 1558c2ecf20Sopenharmony_ci if (rc < 0) { 1568c2ecf20Sopenharmony_ci nvdimm_put_key(key); 1578c2ecf20Sopenharmony_ci return rc; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci nvdimm_put_key(key); 1618c2ecf20Sopenharmony_ci nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int __nvdimm_security_unlock(struct nvdimm *nvdimm) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct device *dev = &nvdimm->dev; 1688c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 1698c2ecf20Sopenharmony_ci struct key *key; 1708c2ecf20Sopenharmony_ci const void *data; 1718c2ecf20Sopenharmony_ci int rc; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* The bus lock should be held at the top level of the call stack */ 1748c2ecf20Sopenharmony_ci lockdep_assert_held(&nvdimm_bus->reconfig_mutex); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (!nvdimm->sec.ops || !nvdimm->sec.ops->unlock 1778c2ecf20Sopenharmony_ci || !nvdimm->sec.flags) 1788c2ecf20Sopenharmony_ci return -EIO; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* No need to go further if security is disabled */ 1818c2ecf20Sopenharmony_ci if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags)) 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { 1858c2ecf20Sopenharmony_ci dev_dbg(dev, "Security operation in progress.\n"); 1868c2ecf20Sopenharmony_ci return -EBUSY; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * If the pre-OS has unlocked the DIMM, attempt to send the key 1918c2ecf20Sopenharmony_ci * from request_key() to the hardware for verification. Failure 1928c2ecf20Sopenharmony_ci * to revalidate the key against the hardware results in a 1938c2ecf20Sopenharmony_ci * freeze of the security configuration. I.e. if the OS does not 1948c2ecf20Sopenharmony_ci * have the key, security is being managed pre-OS. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) { 1978c2ecf20Sopenharmony_ci if (!key_revalidate) 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return nvdimm_key_revalidate(nvdimm); 2018c2ecf20Sopenharmony_ci } else 2028c2ecf20Sopenharmony_ci data = nvdimm_get_key_payload(nvdimm, &key); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci rc = nvdimm->sec.ops->unlock(nvdimm, data); 2058c2ecf20Sopenharmony_ci dev_dbg(dev, "key: %d unlock: %s\n", key_serial(key), 2068c2ecf20Sopenharmony_ci rc == 0 ? "success" : "fail"); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci nvdimm_put_key(key); 2098c2ecf20Sopenharmony_ci nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); 2108c2ecf20Sopenharmony_ci return rc; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ciint nvdimm_security_unlock(struct device *dev) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 2168c2ecf20Sopenharmony_ci int rc; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci nvdimm_bus_lock(dev); 2198c2ecf20Sopenharmony_ci rc = __nvdimm_security_unlock(nvdimm); 2208c2ecf20Sopenharmony_ci nvdimm_bus_unlock(dev); 2218c2ecf20Sopenharmony_ci return rc; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int check_security_state(struct nvdimm *nvdimm) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct device *dev = &nvdimm->dev; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (test_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags)) { 2298c2ecf20Sopenharmony_ci dev_dbg(dev, "Incorrect security state: %#lx\n", 2308c2ecf20Sopenharmony_ci nvdimm->sec.flags); 2318c2ecf20Sopenharmony_ci return -EIO; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { 2358c2ecf20Sopenharmony_ci dev_dbg(dev, "Security operation in progress.\n"); 2368c2ecf20Sopenharmony_ci return -EBUSY; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int security_disable(struct nvdimm *nvdimm, unsigned int keyid) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct device *dev = &nvdimm->dev; 2458c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 2468c2ecf20Sopenharmony_ci struct key *key; 2478c2ecf20Sopenharmony_ci int rc; 2488c2ecf20Sopenharmony_ci const void *data; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* The bus lock should be held at the top level of the call stack */ 2518c2ecf20Sopenharmony_ci lockdep_assert_held(&nvdimm_bus->reconfig_mutex); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (!nvdimm->sec.ops || !nvdimm->sec.ops->disable 2548c2ecf20Sopenharmony_ci || !nvdimm->sec.flags) 2558c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci rc = check_security_state(nvdimm); 2588c2ecf20Sopenharmony_ci if (rc) 2598c2ecf20Sopenharmony_ci return rc; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci data = nvdimm_get_user_key_payload(nvdimm, keyid, 2628c2ecf20Sopenharmony_ci NVDIMM_BASE_KEY, &key); 2638c2ecf20Sopenharmony_ci if (!data) 2648c2ecf20Sopenharmony_ci return -ENOKEY; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci rc = nvdimm->sec.ops->disable(nvdimm, data); 2678c2ecf20Sopenharmony_ci dev_dbg(dev, "key: %d disable: %s\n", key_serial(key), 2688c2ecf20Sopenharmony_ci rc == 0 ? "success" : "fail"); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci nvdimm_put_key(key); 2718c2ecf20Sopenharmony_ci nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); 2728c2ecf20Sopenharmony_ci return rc; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int security_update(struct nvdimm *nvdimm, unsigned int keyid, 2768c2ecf20Sopenharmony_ci unsigned int new_keyid, 2778c2ecf20Sopenharmony_ci enum nvdimm_passphrase_type pass_type) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct device *dev = &nvdimm->dev; 2808c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 2818c2ecf20Sopenharmony_ci struct key *key, *newkey; 2828c2ecf20Sopenharmony_ci int rc; 2838c2ecf20Sopenharmony_ci const void *data, *newdata; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* The bus lock should be held at the top level of the call stack */ 2868c2ecf20Sopenharmony_ci lockdep_assert_held(&nvdimm_bus->reconfig_mutex); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (!nvdimm->sec.ops || !nvdimm->sec.ops->change_key 2898c2ecf20Sopenharmony_ci || !nvdimm->sec.flags) 2908c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci rc = check_security_state(nvdimm); 2938c2ecf20Sopenharmony_ci if (rc) 2948c2ecf20Sopenharmony_ci return rc; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci data = nvdimm_get_user_key_payload(nvdimm, keyid, 2978c2ecf20Sopenharmony_ci NVDIMM_BASE_KEY, &key); 2988c2ecf20Sopenharmony_ci if (!data) 2998c2ecf20Sopenharmony_ci return -ENOKEY; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci newdata = nvdimm_get_user_key_payload(nvdimm, new_keyid, 3028c2ecf20Sopenharmony_ci NVDIMM_NEW_KEY, &newkey); 3038c2ecf20Sopenharmony_ci if (!newdata) { 3048c2ecf20Sopenharmony_ci nvdimm_put_key(key); 3058c2ecf20Sopenharmony_ci return -ENOKEY; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci rc = nvdimm->sec.ops->change_key(nvdimm, data, newdata, pass_type); 3098c2ecf20Sopenharmony_ci dev_dbg(dev, "key: %d %d update%s: %s\n", 3108c2ecf20Sopenharmony_ci key_serial(key), key_serial(newkey), 3118c2ecf20Sopenharmony_ci pass_type == NVDIMM_MASTER ? "(master)" : "(user)", 3128c2ecf20Sopenharmony_ci rc == 0 ? "success" : "fail"); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci nvdimm_put_key(newkey); 3158c2ecf20Sopenharmony_ci nvdimm_put_key(key); 3168c2ecf20Sopenharmony_ci if (pass_type == NVDIMM_MASTER) 3178c2ecf20Sopenharmony_ci nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, 3188c2ecf20Sopenharmony_ci NVDIMM_MASTER); 3198c2ecf20Sopenharmony_ci else 3208c2ecf20Sopenharmony_ci nvdimm->sec.flags = nvdimm_security_flags(nvdimm, 3218c2ecf20Sopenharmony_ci NVDIMM_USER); 3228c2ecf20Sopenharmony_ci return rc; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int security_erase(struct nvdimm *nvdimm, unsigned int keyid, 3268c2ecf20Sopenharmony_ci enum nvdimm_passphrase_type pass_type) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct device *dev = &nvdimm->dev; 3298c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 3308c2ecf20Sopenharmony_ci struct key *key = NULL; 3318c2ecf20Sopenharmony_ci int rc; 3328c2ecf20Sopenharmony_ci const void *data; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* The bus lock should be held at the top level of the call stack */ 3358c2ecf20Sopenharmony_ci lockdep_assert_held(&nvdimm_bus->reconfig_mutex); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (!nvdimm->sec.ops || !nvdimm->sec.ops->erase 3388c2ecf20Sopenharmony_ci || !nvdimm->sec.flags) 3398c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci rc = check_security_state(nvdimm); 3428c2ecf20Sopenharmony_ci if (rc) 3438c2ecf20Sopenharmony_ci return rc; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (!test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.ext_flags) 3468c2ecf20Sopenharmony_ci && pass_type == NVDIMM_MASTER) { 3478c2ecf20Sopenharmony_ci dev_dbg(dev, 3488c2ecf20Sopenharmony_ci "Attempt to secure erase in wrong master state.\n"); 3498c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci data = nvdimm_get_user_key_payload(nvdimm, keyid, 3538c2ecf20Sopenharmony_ci NVDIMM_BASE_KEY, &key); 3548c2ecf20Sopenharmony_ci if (!data) 3558c2ecf20Sopenharmony_ci return -ENOKEY; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci rc = nvdimm->sec.ops->erase(nvdimm, data, pass_type); 3588c2ecf20Sopenharmony_ci dev_dbg(dev, "key: %d erase%s: %s\n", key_serial(key), 3598c2ecf20Sopenharmony_ci pass_type == NVDIMM_MASTER ? "(master)" : "(user)", 3608c2ecf20Sopenharmony_ci rc == 0 ? "success" : "fail"); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci nvdimm_put_key(key); 3638c2ecf20Sopenharmony_ci nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); 3648c2ecf20Sopenharmony_ci return rc; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int security_overwrite(struct nvdimm *nvdimm, unsigned int keyid) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct device *dev = &nvdimm->dev; 3708c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 3718c2ecf20Sopenharmony_ci struct key *key = NULL; 3728c2ecf20Sopenharmony_ci int rc; 3738c2ecf20Sopenharmony_ci const void *data; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* The bus lock should be held at the top level of the call stack */ 3768c2ecf20Sopenharmony_ci lockdep_assert_held(&nvdimm_bus->reconfig_mutex); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (!nvdimm->sec.ops || !nvdimm->sec.ops->overwrite 3798c2ecf20Sopenharmony_ci || !nvdimm->sec.flags) 3808c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci rc = check_security_state(nvdimm); 3838c2ecf20Sopenharmony_ci if (rc) 3848c2ecf20Sopenharmony_ci return rc; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci data = nvdimm_get_user_key_payload(nvdimm, keyid, 3878c2ecf20Sopenharmony_ci NVDIMM_BASE_KEY, &key); 3888c2ecf20Sopenharmony_ci if (!data) 3898c2ecf20Sopenharmony_ci return -ENOKEY; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci rc = nvdimm->sec.ops->overwrite(nvdimm, data); 3928c2ecf20Sopenharmony_ci dev_dbg(dev, "key: %d overwrite submission: %s\n", key_serial(key), 3938c2ecf20Sopenharmony_ci rc == 0 ? "success" : "fail"); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci nvdimm_put_key(key); 3968c2ecf20Sopenharmony_ci if (rc == 0) { 3978c2ecf20Sopenharmony_ci set_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags); 3988c2ecf20Sopenharmony_ci set_bit(NDD_WORK_PENDING, &nvdimm->flags); 3998c2ecf20Sopenharmony_ci set_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags); 4008c2ecf20Sopenharmony_ci /* 4018c2ecf20Sopenharmony_ci * Make sure we don't lose device while doing overwrite 4028c2ecf20Sopenharmony_ci * query. 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_ci get_device(dev); 4058c2ecf20Sopenharmony_ci queue_delayed_work(system_wq, &nvdimm->dwork, 0); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return rc; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_civoid __nvdimm_security_overwrite_query(struct nvdimm *nvdimm) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nvdimm->dev); 4148c2ecf20Sopenharmony_ci int rc; 4158c2ecf20Sopenharmony_ci unsigned int tmo; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* The bus lock should be held at the top level of the call stack */ 4188c2ecf20Sopenharmony_ci lockdep_assert_held(&nvdimm_bus->reconfig_mutex); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* 4218c2ecf20Sopenharmony_ci * Abort and release device if we no longer have the overwrite 4228c2ecf20Sopenharmony_ci * flag set. It means the work has been canceled. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci if (!test_bit(NDD_WORK_PENDING, &nvdimm->flags)) 4258c2ecf20Sopenharmony_ci return; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci tmo = nvdimm->sec.overwrite_tmo; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (!nvdimm->sec.ops || !nvdimm->sec.ops->query_overwrite 4308c2ecf20Sopenharmony_ci || !nvdimm->sec.flags) 4318c2ecf20Sopenharmony_ci return; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci rc = nvdimm->sec.ops->query_overwrite(nvdimm); 4348c2ecf20Sopenharmony_ci if (rc == -EBUSY) { 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* setup delayed work again */ 4378c2ecf20Sopenharmony_ci tmo += 10; 4388c2ecf20Sopenharmony_ci queue_delayed_work(system_wq, &nvdimm->dwork, tmo * HZ); 4398c2ecf20Sopenharmony_ci nvdimm->sec.overwrite_tmo = min(15U * 60U, tmo); 4408c2ecf20Sopenharmony_ci return; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (rc < 0) 4448c2ecf20Sopenharmony_ci dev_dbg(&nvdimm->dev, "overwrite failed\n"); 4458c2ecf20Sopenharmony_ci else 4468c2ecf20Sopenharmony_ci dev_dbg(&nvdimm->dev, "overwrite completed\n"); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * Mark the overwrite work done and update dimm security flags, 4508c2ecf20Sopenharmony_ci * then send a sysfs event notification to wake up userspace 4518c2ecf20Sopenharmony_ci * poll threads to picked up the changed state. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci nvdimm->sec.overwrite_tmo = 0; 4548c2ecf20Sopenharmony_ci clear_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags); 4558c2ecf20Sopenharmony_ci clear_bit(NDD_WORK_PENDING, &nvdimm->flags); 4568c2ecf20Sopenharmony_ci nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); 4578c2ecf20Sopenharmony_ci nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER); 4588c2ecf20Sopenharmony_ci if (nvdimm->sec.overwrite_state) 4598c2ecf20Sopenharmony_ci sysfs_notify_dirent(nvdimm->sec.overwrite_state); 4608c2ecf20Sopenharmony_ci put_device(&nvdimm->dev); 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_civoid nvdimm_security_overwrite_query(struct work_struct *work) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = 4668c2ecf20Sopenharmony_ci container_of(work, typeof(*nvdimm), dwork.work); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci nvdimm_bus_lock(&nvdimm->dev); 4698c2ecf20Sopenharmony_ci __nvdimm_security_overwrite_query(nvdimm); 4708c2ecf20Sopenharmony_ci nvdimm_bus_unlock(&nvdimm->dev); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci#define OPS \ 4748c2ecf20Sopenharmony_ci C( OP_FREEZE, "freeze", 1), \ 4758c2ecf20Sopenharmony_ci C( OP_DISABLE, "disable", 2), \ 4768c2ecf20Sopenharmony_ci C( OP_UPDATE, "update", 3), \ 4778c2ecf20Sopenharmony_ci C( OP_ERASE, "erase", 2), \ 4788c2ecf20Sopenharmony_ci C( OP_OVERWRITE, "overwrite", 2), \ 4798c2ecf20Sopenharmony_ci C( OP_MASTER_UPDATE, "master_update", 3), \ 4808c2ecf20Sopenharmony_ci C( OP_MASTER_ERASE, "master_erase", 2) 4818c2ecf20Sopenharmony_ci#undef C 4828c2ecf20Sopenharmony_ci#define C(a, b, c) a 4838c2ecf20Sopenharmony_cienum nvdimmsec_op_ids { OPS }; 4848c2ecf20Sopenharmony_ci#undef C 4858c2ecf20Sopenharmony_ci#define C(a, b, c) { b, c } 4868c2ecf20Sopenharmony_cistatic struct { 4878c2ecf20Sopenharmony_ci const char *name; 4888c2ecf20Sopenharmony_ci int args; 4898c2ecf20Sopenharmony_ci} ops[] = { OPS }; 4908c2ecf20Sopenharmony_ci#undef C 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci#define SEC_CMD_SIZE 32 4938c2ecf20Sopenharmony_ci#define KEY_ID_SIZE 10 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cissize_t nvdimm_security_store(struct device *dev, const char *buf, size_t len) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 4988c2ecf20Sopenharmony_ci ssize_t rc; 4998c2ecf20Sopenharmony_ci char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1], 5008c2ecf20Sopenharmony_ci nkeystr[KEY_ID_SIZE+1]; 5018c2ecf20Sopenharmony_ci unsigned int key, newkey; 5028c2ecf20Sopenharmony_ci int i; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s" 5058c2ecf20Sopenharmony_ci " %"__stringify(KEY_ID_SIZE)"s" 5068c2ecf20Sopenharmony_ci " %"__stringify(KEY_ID_SIZE)"s", 5078c2ecf20Sopenharmony_ci cmd, keystr, nkeystr); 5088c2ecf20Sopenharmony_ci if (rc < 1) 5098c2ecf20Sopenharmony_ci return -EINVAL; 5108c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ops); i++) 5118c2ecf20Sopenharmony_ci if (sysfs_streq(cmd, ops[i].name)) 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci if (i >= ARRAY_SIZE(ops)) 5148c2ecf20Sopenharmony_ci return -EINVAL; 5158c2ecf20Sopenharmony_ci if (ops[i].args > 1) 5168c2ecf20Sopenharmony_ci rc = kstrtouint(keystr, 0, &key); 5178c2ecf20Sopenharmony_ci if (rc >= 0 && ops[i].args > 2) 5188c2ecf20Sopenharmony_ci rc = kstrtouint(nkeystr, 0, &newkey); 5198c2ecf20Sopenharmony_ci if (rc < 0) 5208c2ecf20Sopenharmony_ci return rc; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (i == OP_FREEZE) { 5238c2ecf20Sopenharmony_ci dev_dbg(dev, "freeze\n"); 5248c2ecf20Sopenharmony_ci rc = nvdimm_security_freeze(nvdimm); 5258c2ecf20Sopenharmony_ci } else if (i == OP_DISABLE) { 5268c2ecf20Sopenharmony_ci dev_dbg(dev, "disable %u\n", key); 5278c2ecf20Sopenharmony_ci rc = security_disable(nvdimm, key); 5288c2ecf20Sopenharmony_ci } else if (i == OP_UPDATE || i == OP_MASTER_UPDATE) { 5298c2ecf20Sopenharmony_ci dev_dbg(dev, "%s %u %u\n", ops[i].name, key, newkey); 5308c2ecf20Sopenharmony_ci rc = security_update(nvdimm, key, newkey, i == OP_UPDATE 5318c2ecf20Sopenharmony_ci ? NVDIMM_USER : NVDIMM_MASTER); 5328c2ecf20Sopenharmony_ci } else if (i == OP_ERASE || i == OP_MASTER_ERASE) { 5338c2ecf20Sopenharmony_ci dev_dbg(dev, "%s %u\n", ops[i].name, key); 5348c2ecf20Sopenharmony_ci if (atomic_read(&nvdimm->busy)) { 5358c2ecf20Sopenharmony_ci dev_dbg(dev, "Unable to secure erase while DIMM active.\n"); 5368c2ecf20Sopenharmony_ci return -EBUSY; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci rc = security_erase(nvdimm, key, i == OP_ERASE 5398c2ecf20Sopenharmony_ci ? NVDIMM_USER : NVDIMM_MASTER); 5408c2ecf20Sopenharmony_ci } else if (i == OP_OVERWRITE) { 5418c2ecf20Sopenharmony_ci dev_dbg(dev, "overwrite %u\n", key); 5428c2ecf20Sopenharmony_ci if (atomic_read(&nvdimm->busy)) { 5438c2ecf20Sopenharmony_ci dev_dbg(dev, "Unable to overwrite while DIMM active.\n"); 5448c2ecf20Sopenharmony_ci return -EBUSY; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci rc = security_overwrite(nvdimm, key); 5478c2ecf20Sopenharmony_ci } else 5488c2ecf20Sopenharmony_ci return -EINVAL; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (rc == 0) 5518c2ecf20Sopenharmony_ci rc = len; 5528c2ecf20Sopenharmony_ci return rc; 5538c2ecf20Sopenharmony_ci} 554