From patchwork Fri Nov 9 22:13:51 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Jiang X-Patchwork-Id: 10676621 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DB3B9139B for ; Fri, 9 Nov 2018 22:13:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CBA212F0B5 for ; Fri, 9 Nov 2018 22:13:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id BFEDE2F1A5; Fri, 9 Nov 2018 22:13:53 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 67C9E2F0B5 for ; Fri, 9 Nov 2018 22:13:53 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 2BAA02118EF61; Fri, 9 Nov 2018 14:13:53 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.88; helo=mga01.intel.com; envelope-from=dave.jiang@intel.com; receiver=linux-nvdimm@lists.01.org Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 4DD6D21184E66 for ; Fri, 9 Nov 2018 14:13:52 -0800 (PST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga101.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Nov 2018 14:13:51 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,484,1534834800"; d="scan'208";a="89961708" Received: from djiang5-desk3.ch.intel.com ([143.182.136.93]) by orsmga006.jf.intel.com with ESMTP; 09 Nov 2018 14:13:51 -0800 Subject: [PATCH 01/11] keys-encrypted: add nvdimm key format type to encrypted keys From: Dave Jiang To: dan.j.williams@intel.com, zohar@linux.vnet.ibm.com Date: Fri, 09 Nov 2018 15:13:51 -0700 Message-ID: <154180163107.70506.6894134402057805078.stgit@djiang5-desk3.ch.intel.com> In-Reply-To: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> References: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-nvdimm@lists.01.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Adding nvdimm key format type to encrypted keys in order to limit the size of the key to 32bytes. Signed-off-by: Dave Jiang --- security/keys/encrypted-keys/encrypted.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index d92cbf9687c3..182b4f136bdf 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -45,6 +45,7 @@ static const char hmac_alg[] = "hmac(sha256)"; static const char blkcipher_alg[] = "cbc(aes)"; static const char key_format_default[] = "default"; static const char key_format_ecryptfs[] = "ecryptfs"; +static const char key_format_nvdimm[] = "nvdimm"; static unsigned int ivsize; static int blksize; @@ -54,6 +55,7 @@ static int blksize; #define HASH_SIZE SHA256_DIGEST_SIZE #define MAX_DATA_SIZE 4096 #define MIN_DATA_SIZE 20 +#define KEY_NVDIMM_PAYLOAD_LEN 32 static struct crypto_shash *hash_tfm; @@ -62,12 +64,13 @@ enum { }; enum { - Opt_error = -1, Opt_default, Opt_ecryptfs + Opt_error = -1, Opt_default, Opt_ecryptfs, Opt_nvdimm }; static const match_table_t key_format_tokens = { {Opt_default, "default"}, {Opt_ecryptfs, "ecryptfs"}, + {Opt_nvdimm, "nvdimm"}, {Opt_error, NULL} }; @@ -195,6 +198,7 @@ static int datablob_parse(char *datablob, const char **format, key_format = match_token(p, key_format_tokens, args); switch (key_format) { case Opt_ecryptfs: + case Opt_nvdimm: case Opt_default: *format = p; *master_desc = strsep(&datablob, " \t"); @@ -625,15 +629,22 @@ static struct encrypted_key_payload *encrypted_key_alloc(struct key *key, format_len = (!format) ? strlen(key_format_default) : strlen(format); decrypted_datalen = dlen; payload_datalen = decrypted_datalen; - if (format && !strcmp(format, key_format_ecryptfs)) { - if (dlen != ECRYPTFS_MAX_KEY_BYTES) { - pr_err("encrypted_key: keylen for the ecryptfs format " - "must be equal to %d bytes\n", - ECRYPTFS_MAX_KEY_BYTES); - return ERR_PTR(-EINVAL); + if (format) { + if (!strcmp(format, key_format_ecryptfs)) { + if (dlen != ECRYPTFS_MAX_KEY_BYTES) { + pr_err("encrypted_key: keylen for the ecryptfs format must be equal to %d bytes\n", + ECRYPTFS_MAX_KEY_BYTES); + return ERR_PTR(-EINVAL); + } + decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES; + payload_datalen = sizeof(struct ecryptfs_auth_tok); + } else if (!strcmp(format, key_format_nvdimm)) { + if (decrypted_datalen != KEY_NVDIMM_PAYLOAD_LEN) { + pr_err("encrypted_key: nvdimm key payload incorrect length: %d\n", + decrypted_datalen); + return ERR_PTR(-EINVAL); + } } - decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES; - payload_datalen = sizeof(struct ecryptfs_auth_tok); } encrypted_datalen = roundup(decrypted_datalen, blksize); From patchwork Fri Nov 9 22:13:56 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Jiang X-Patchwork-Id: 10676623 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 848BA139B for ; Fri, 9 Nov 2018 22:14:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 748E62F1A5 for ; Fri, 9 Nov 2018 22:14:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 684902F317; Fri, 9 Nov 2018 22:14:00 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 54A6D2F1A5 for ; Fri, 9 Nov 2018 22:13:59 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 4C1202118EF60; Fri, 9 Nov 2018 14:13:59 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=134.134.136.24; helo=mga09.intel.com; envelope-from=dave.jiang@intel.com; receiver=linux-nvdimm@lists.01.org Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id CBBC22118EF5C for ; Fri, 9 Nov 2018 14:13:57 -0800 (PST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Nov 2018 14:13:57 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,484,1534834800"; d="scan'208";a="88126740" Received: from djiang5-desk3.ch.intel.com ([143.182.136.93]) by orsmga007.jf.intel.com with ESMTP; 09 Nov 2018 14:13:56 -0800 Subject: [PATCH 02/11] libnvdimm/security: change clear text nvdimm keys to encrypted keys From: Dave Jiang To: dan.j.williams@intel.com, zohar@linux.vnet.ibm.com Date: Fri, 09 Nov 2018 15:13:56 -0700 Message-ID: <154180163666.70506.8805433934495072699.stgit@djiang5-desk3.ch.intel.com> In-Reply-To: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> References: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-nvdimm@lists.01.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP In order to make nvdimm more secure, encrypted keys will be used instead of clear text keys. A master key will be created to seal encrypted nvdimm keys. The master key can be a trusted key generated from TPM 2.0 or a less secure user key. In the process of this conversion, the kernel cached key will be removed in order to simplify the verification process. The hardware will be used to verify the decrypted user payload directly. Signed-off-by: Dave Jiang --- Documentation/nvdimm/security.txt | 29 ++- drivers/nvdimm/dimm.c | 3 drivers/nvdimm/dimm_devs.c | 2 drivers/nvdimm/nd-core.h | 3 drivers/nvdimm/nd.h | 5 - drivers/nvdimm/security.c | 316 ++++++++++--------------------------- 6 files changed, 108 insertions(+), 250 deletions(-) diff --git a/Documentation/nvdimm/security.txt b/Documentation/nvdimm/security.txt index 50cbb6cb96a1..11240ce48755 100644 --- a/Documentation/nvdimm/security.txt +++ b/Documentation/nvdimm/security.txt @@ -39,34 +39,39 @@ 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 or padded to 32bytes. This is +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 and retrieved from userspace. It is up to the user to provide an upcall application to retrieve the key in whatever fashion meets their security requirements. -A nvdimm user logon key has the description format of: +A nvdimm encrypted key has the description format of: nvdimm: +See Documentation/security/keys/trusted-encrypted.rst for details on encrypted +keys. The encrypted key for the DIMM is generated from a master key that is +either a trusted key created via TPM2 or a less secure user key. The payload +for the nvdimm encrypted key is randomly generated by the kernel and is not +visible to the user. The user is responsible for retaining the encrypted key +blob generated from the encrypted key to be loaded at the next boot session. + 4. Unlocking ------------ When the DIMMs are being enumerated by the kernel, the kernel will attempt to -retrieve the key from its keyring. If that fails, it will attempt to acquire the key from the userspace upcall function. This is the only time a locked DIMM can be unlocked. Once unlocked, the DIMM will remain unlocked until reboot. 5. Update --------- -When doing an update, it is expected that the new key with the 64bit payload of -format described above is added via the keyutils API or utility. The update -command written to the sysfs attribute will be with the format: +When doing an update, it is expected that the new key is added via the +keyutils API or utility. The update command written to the sysfs attribute +will be with the format: update -It is expected that a user logon key has been injected via keyutils to provide -the payload for the update operation. The kernel will take the new user key, -attempt the update operation with the nvdimm, and replace the existing key's -payload with the new passphrase. +It is expected that a encrypted key has been injected via keyutils to provide +the payload for the update operation. The kernel will take the new user key +and the old user key to attempt the update operation. If there is no old key id due to a security enabling, then a 0 should be passed in. If a nvdimm has an existing passphrase, then an "old" key should @@ -80,7 +85,7 @@ frozen by a user with root privelege. 7. Disable ---------- The security disable command format is: -disable +disable An "old" key with the passphrase payload that is tied to the nvdimm should be injected with a key description that does not have the "nvdimm:" prefix and @@ -89,7 +94,7 @@ its keyid should be passed in via sysfs. 8. Secure Erase --------------- The command format for doing a secure erase is: -erase +erase An "old" key with the passphrase payload that is tied to the nvdimm should be injected with a key description that does not have the "nvdimm:" prefix and diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c index 225a8474c44a..c3886a22b5e4 100644 --- a/drivers/nvdimm/dimm.c +++ b/drivers/nvdimm/dimm.c @@ -112,9 +112,6 @@ static int nvdimm_probe(struct device *dev) static int nvdimm_remove(struct device *dev) { struct nvdimm_drvdata *ndd = dev_get_drvdata(dev); - struct nvdimm *nvdimm = to_nvdimm(dev); - - nvdimm_security_release(nvdimm); if (!ndd) return 0; diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 5d34251c4f3b..269f401a5e68 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -485,7 +485,7 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_dat nvdimm->num_flush = num_flush; nvdimm->flush_wpq = flush_wpq; atomic_set(&nvdimm->busy, 0); - mutex_init(&nvdimm->key_mutex); + mutex_init(&nvdimm->sec_mutex); dev = &nvdimm->dev; dev_set_name(dev, "nmem%d", nvdimm->id); dev->parent = &nvdimm_bus->dev; diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index 6e2877996a9b..cfb87f71db8d 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -45,8 +45,7 @@ struct nvdimm { const char *dimm_id; const struct nvdimm_security_ops *security_ops; enum nvdimm_security_state state; - struct key *key; - struct mutex key_mutex; + struct mutex sec_mutex; }; /** diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 892f127bfd38..f8d8f0a2a40d 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -428,7 +428,6 @@ bool pmem_should_map_pages(struct device *dev); #ifdef CONFIG_NVDIMM_SECURITY int nvdimm_security_unlock_dimm(struct nvdimm *nvdimm); -void nvdimm_security_release(struct nvdimm *nvdimm); int nvdimm_security_get_state(struct nvdimm *nvdimm); int nvdimm_security_change_key(struct nvdimm *nvdimm, unsigned int old_keyid, unsigned int new_keyid); @@ -441,10 +440,6 @@ static inline int nvdimm_security_unlock_dimm(struct nvdimm *nvdimm) return 0; } -static inline void nvdimm_security_release(struct nvdimm *nvdimm) -{ -} - static inline int nvdimm_security_get_state(struct nvdimm *nvdimm) { return -EOPNOTSUPP; diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c index 5aacc590b4c0..ee741199d623 100644 --- a/drivers/nvdimm/security.c +++ b/drivers/nvdimm/security.c @@ -10,43 +10,10 @@ #include #include #include +#include #include "nd-core.h" #include "nd.h" -/* - * Replacing the user key with a kernel key. The function expects that - * we hold the sem for the key passed in. The function will release that - * sem when done process. We will also hold the sem for the valid new key - * returned. - */ -static struct key *make_kernel_key(struct key *key) -{ - struct key *new_key; - struct user_key_payload *payload; - int rc; - - new_key = key_alloc(&key_type_logon, key->description, - GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), - KEY_POS_ALL & ~KEY_POS_SETATTR, - KEY_ALLOC_NOT_IN_QUOTA, NULL); - if (IS_ERR(new_key)) - return NULL; - - payload = key->payload.data[0]; - rc = key_instantiate_and_link(new_key, payload->data, - payload->datalen, NULL, NULL); - up_read(&key->sem); - if (rc < 0) { - key_put(new_key); - return NULL; - } - - key_put(key); - - down_read(&new_key->sem); - return new_key; -} - /* * Retrieve user injected key */ @@ -61,6 +28,10 @@ static struct key *nvdimm_lookup_user_key(struct device *dev, 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)); return key; @@ -76,7 +47,7 @@ static struct key *nvdimm_request_key(struct nvdimm *nvdimm) struct device *dev = &nvdimm->dev; sprintf(desc, "%s%s", NVDIMM_PREFIX, nvdimm->dimm_id); - key = request_key(&key_type_logon, desc, ""); + key = request_key(&key_type_encrypted, desc, ""); if (IS_ERR(key)) { if (PTR_ERR(key) == -ENOKEY) dev_warn(dev, "request_key() found no key\n"); @@ -88,52 +59,6 @@ static struct key *nvdimm_request_key(struct nvdimm *nvdimm) return key; } -struct key *nvdimm_get_and_verify_key(struct nvdimm *nvdimm, - unsigned int user_key_id) -{ - int rc; - struct key *user_key, *key; - struct device *dev = &nvdimm->dev; - struct user_key_payload *upayload, *payload; - - lockdep_assert_held(&nvdimm->key_mutex); - key = nvdimm->key; - if (!key) { - dev_dbg(dev, "No cached kernel key\n"); - return ERR_PTR(-EAGAIN);; - } - dev_dbg(dev, "cached_key: %#x\n", key_serial(key)); - - user_key = nvdimm_lookup_user_key(dev, user_key_id); - if (!user_key) { - dev_dbg(dev, "Old user key lookup failed\n"); - return ERR_PTR(-EINVAL); - } - dev_dbg(dev, "user_key: %#x\n", key_serial(user_key)); - - down_read(&key->sem); - down_read(&user_key->sem); - payload = key->payload.data[0]; - upayload = user_key->payload.data[0]; - - rc = memcmp(payload->data, upayload->data, NVDIMM_PASSPHRASE_LEN); - up_read(&user_key->sem); - key_put(user_key); - up_read(&key->sem); - - if (rc != 0) { - dev_warn(dev, "Supplied old user key fails check.\n"); - return ERR_PTR(-EINVAL); - } - return key; -} - -static void key_destroy(struct key *key) -{ - key_invalidate(key); - key_put(key); -} - int nvdimm_security_get_state(struct nvdimm *nvdimm) { if (!nvdimm->security_ops) @@ -146,14 +71,15 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid) { int rc = 0; struct key *key; - struct user_key_payload *payload; + struct encrypted_key_payload *epayload; struct device *dev = &nvdimm->dev; + void *data; if (!nvdimm->security_ops) return -EOPNOTSUPP; nvdimm_bus_lock(dev); - mutex_lock(&nvdimm->key_mutex); + mutex_lock(&nvdimm->sec_mutex); if (atomic_read(&nvdimm->busy)) { dev_warn(dev, "Unable to secure erase while DIMM active.\n"); rc = -EBUSY; @@ -166,26 +92,23 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid) goto out; } - /* look for a key from cached key if exists */ - key = nvdimm_get_and_verify_key(nvdimm, keyid); - if (IS_ERR(key)) { + key = nvdimm_lookup_user_key(dev, keyid); + if (!key) { dev_dbg(dev, "Unable to get and verify key\n"); - rc = PTR_ERR(key); + rc = -ENOKEY; goto out; } down_read(&key->sem); - payload = key->payload.data[0]; + epayload = dereference_key_locked(key); + data = epayload->decrypted_data; rc = nvdimm->security_ops->erase(nvdimm, - (const struct nvdimm_key_data *)payload->data); + (const struct nvdimm_key_data *)data); up_read(&key->sem); - - /* remove key since secure erase kills the passphrase */ - key_destroy(key); - nvdimm->key = NULL; + key_put(key); out: - mutex_unlock(&nvdimm->key_mutex); + mutex_unlock(&nvdimm->sec_mutex); nvdimm_bus_unlock(dev); nvdimm_security_get_state(nvdimm); return rc; @@ -201,11 +124,15 @@ int nvdimm_security_freeze_lock(struct nvdimm *nvdimm) if (nvdimm->state == NVDIMM_SECURITY_UNSUPPORTED) return -EOPNOTSUPP; + mutex_lock(&nvdimm->sec_mutex); rc = nvdimm->security_ops->freeze_lock(nvdimm); - if (rc < 0) + if (rc < 0) { + mutex_unlock(&nvdimm->sec_mutex); return rc; + } nvdimm_security_get_state(nvdimm); + mutex_unlock(&nvdimm->sec_mutex); return 0; } @@ -213,8 +140,9 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid) { int rc; struct key *key; - struct user_key_payload *payload; + struct encrypted_key_payload *epayload; struct device *dev = &nvdimm->dev; + void *data; if (!nvdimm->security_ops) return -EOPNOTSUPP; @@ -222,57 +150,52 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid) if (nvdimm->state == NVDIMM_SECURITY_UNSUPPORTED) return -EOPNOTSUPP; - mutex_lock(&nvdimm->key_mutex); + mutex_lock(&nvdimm->sec_mutex); /* look for a key from cached key */ - key = nvdimm_get_and_verify_key(nvdimm, keyid); - if (IS_ERR(key)) { - mutex_unlock(&nvdimm->key_mutex); - return PTR_ERR(key); + key = nvdimm_lookup_user_key(dev, keyid); + if (!key) { + mutex_unlock(&nvdimm->sec_mutex); + return -ENOKEY; } down_read(&key->sem); - payload = key->payload.data[0]; + epayload = dereference_key_locked(key); + data = epayload->decrypted_data; rc = nvdimm->security_ops->disable(nvdimm, - (const struct nvdimm_key_data *)payload->data); + (const struct nvdimm_key_data *)data); up_read(&key->sem); - if (rc < 0) { + if (rc < 0) dev_warn(dev, "security disable failed\n"); - goto out; - } - /* If we succeed then remove the key */ - key_destroy(key); - nvdimm->key = NULL; - - out: - mutex_unlock(&nvdimm->key_mutex); + key_put(key); nvdimm_security_get_state(nvdimm); + mutex_unlock(&nvdimm->sec_mutex); return rc; } -static int nvdimm_self_verify_key(struct nvdimm *nvdimm) +static struct key *nvdimm_self_verify_key(struct nvdimm *nvdimm) { struct key *key; - struct user_key_payload *payload; + struct encrypted_key_payload *epayload; void *data; int rc; - lockdep_assert_held(&nvdimm->key_mutex); - + lockdep_assert_held(&nvdimm->sec_mutex); key = nvdimm_request_key(nvdimm); if (!key) - return -ENOKEY; + return NULL; + + down_read(&key->sem); + epayload = dereference_key_locked(key); + data = epayload->decrypted_data; - if (key->datalen != NVDIMM_PASSPHRASE_LEN) { + if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) { key_put(key); - return -EINVAL; + key = NULL; + goto out; } - down_read(&key->sem); - payload = key->payload.data[0]; - data = payload->data; - /* * We send the same key to the hardware as new and old key to * verify that the key is good. @@ -280,19 +203,21 @@ static int nvdimm_self_verify_key(struct nvdimm *nvdimm) rc = nvdimm->security_ops->change_key(nvdimm, data, data); if (rc < 0) { key_put(key); - return rc; + key = NULL; } + +out: up_read(&key->sem); - nvdimm->key = key; - return 0; + return key; } int nvdimm_security_unlock_dimm(struct nvdimm *nvdimm) { - struct key *key; - int rc = -ENXIO; - struct user_key_payload *payload; + struct key *key = NULL; + int rc; + struct encrypted_key_payload *epayload; struct device *dev = &nvdimm->dev; + void *data; if (!nvdimm->security_ops) return 0; @@ -301,7 +226,7 @@ int nvdimm_security_unlock_dimm(struct nvdimm *nvdimm) nvdimm->state == NVDIMM_SECURITY_DISABLED) return 0; - mutex_lock(&nvdimm->key_mutex); + mutex_lock(&nvdimm->sec_mutex); /* * If the pre-OS has unlocked the DIMM, we will attempt to send * the key from request_key() to the hardware for verification. @@ -310,60 +235,50 @@ int nvdimm_security_unlock_dimm(struct nvdimm *nvdimm) * other security operations. */ if (nvdimm->state == NVDIMM_SECURITY_UNLOCKED) { - rc = nvdimm_self_verify_key(nvdimm); - if (rc < 0) { + key = nvdimm_self_verify_key(nvdimm); + if (!key) { rc = nvdimm_security_freeze_lock(nvdimm); - mutex_unlock(&nvdimm->key_mutex); - return rc; + mutex_unlock(&nvdimm->sec_mutex); + return -ENOKEY; } } - key = nvdimm->key; - if (!key) { + if (!key) key = nvdimm_request_key(nvdimm); - if (key && key->datalen != NVDIMM_PASSPHRASE_LEN) { - key_put(key); - key = NULL; - rc = -EINVAL; - } - } + if (!key) { - mutex_unlock(&nvdimm->key_mutex); - return rc; + mutex_unlock(&nvdimm->sec_mutex); + return -ENOKEY; } dev_dbg(dev, "%s: key: %#x\n", __func__, key_serial(key)); down_read(&key->sem); - payload = key->payload.data[0]; + epayload = dereference_key_locked(key); + data = epayload->decrypted_data; + + if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) { + up_read(&key->sem); + key_put(key); + mutex_unlock(&nvdimm->sec_mutex); + return -EINVAL; + } + rc = nvdimm->security_ops->unlock(nvdimm, - (const struct nvdimm_key_data *)payload->data); + (const struct nvdimm_key_data *)data); up_read(&key->sem); if (rc == 0) { - if (!nvdimm->key) - nvdimm->key = key; nvdimm->state = NVDIMM_SECURITY_UNLOCKED; dev_dbg(dev, "DIMM %s unlocked\n", dev_name(dev)); - } else { - key_invalidate(key); - key_put(key); - nvdimm->key = NULL; + } else dev_warn(dev, "Failed to unlock dimm: %s\n", dev_name(dev)); - } - mutex_unlock(&nvdimm->key_mutex); + key_put(key); nvdimm_security_get_state(nvdimm); + mutex_unlock(&nvdimm->sec_mutex); return rc; } -void nvdimm_security_release(struct nvdimm *nvdimm) -{ - mutex_lock(&nvdimm->key_mutex); - key_put(nvdimm->key); - nvdimm->key = NULL; - mutex_unlock(&nvdimm->key_mutex); -} - int nvdimm_security_change_key(struct nvdimm *nvdimm, unsigned int old_keyid, unsigned int new_keyid) { @@ -371,7 +286,7 @@ int nvdimm_security_change_key(struct nvdimm *nvdimm, struct key *key, *old_key; void *old_data = NULL, *new_data; struct device *dev = &nvdimm->dev; - struct user_key_payload *payload, *old_payload; + struct encrypted_key_payload *epayload, *old_epayload; if (!nvdimm->security_ops) return -EOPNOTSUPP; @@ -379,15 +294,10 @@ int nvdimm_security_change_key(struct nvdimm *nvdimm, if (nvdimm->state == NVDIMM_SECURITY_FROZEN) return -EBUSY; - mutex_lock(&nvdimm->key_mutex); + mutex_lock(&nvdimm->sec_mutex); /* look for a key from cached key if exists */ - old_key = nvdimm_get_and_verify_key(nvdimm, old_keyid); - if (IS_ERR(old_key) && PTR_ERR(old_key) == -EAGAIN) - old_key = NULL; - else if (IS_ERR(old_key)) { - mutex_unlock(&nvdimm->key_mutex); - return PTR_ERR(old_key); - } else + old_key = nvdimm_lookup_user_key(dev, old_keyid); + if (old_key) dev_dbg(dev, "%s: old key: %#x\n", __func__, key_serial(old_key)); @@ -402,76 +312,28 @@ int nvdimm_security_change_key(struct nvdimm *nvdimm, dev_dbg(dev, "%s: new key: %#x\n", __func__, key_serial(key)); down_read(&key->sem); - payload = key->payload.data[0]; - if (payload->datalen != NVDIMM_PASSPHRASE_LEN) { - rc = -EINVAL; - up_read(&key->sem); - goto out; - } - - /* - * Since there is no existing key this user key will become the - * kernel's key. - */ - if (!old_key) { - key = make_kernel_key(key); - if (!key) { - rc = -ENOMEM; - goto out; - } - } - - /* - * We don't need to release key->sem here because - * make_kernel_key() will have upgraded the user key to kernel - * and handled the semaphore handoff. - */ - payload = key->payload.data[0]; + epayload = dereference_key_locked(key); + new_data = epayload->decrypted_data; if (old_key) { down_read(&old_key->sem); - old_payload = old_key->payload.data[0]; - old_data = old_payload->data; + old_epayload = dereference_key_locked(old_key); + old_data = old_epayload->decrypted_data; } - new_data = payload->data; - rc = nvdimm->security_ops->change_key(nvdimm, old_data, new_data); if (rc) dev_warn(dev, "key update failed: %d\n", rc); - if (old_key) { + if (old_key) up_read(&old_key->sem); - /* - * With the key update done via hardware, we no longer need - * the old payload and need to replace it with the new - * payload. key_update() will acquire write sem of the - * old key and update with new data. - */ - if (rc == 0) { - rc = key_update(make_key_ref(old_key, 1), new_data, - old_key->datalen); - if (rc < 0) { - dev_warn(dev, - "kernel key update failed: %d\n", rc); - key_destroy(old_key); - nvdimm->key = NULL; - } - } - } up_read(&key->sem); - - if (!old_key) { - if (rc == 0) { - dev_dbg(dev, "key cached: %#x\n", key_serial(key)); - nvdimm->key = key; - } else - key_destroy(key); - } nvdimm_security_get_state(nvdimm); out: - mutex_unlock(&nvdimm->key_mutex); + mutex_unlock(&nvdimm->sec_mutex); + key_put(old_key); + key_put(key); return rc; } From patchwork Fri Nov 9 22:14:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Jiang X-Patchwork-Id: 10676625 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C894A139B for ; Fri, 9 Nov 2018 22:14:04 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B9CCA2F1A5 for ; Fri, 9 Nov 2018 22:14:04 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id AE1692F317; Fri, 9 Nov 2018 22:14:04 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 6B7802F1A5 for ; Fri, 9 Nov 2018 22:14:04 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 630D321A00AE6; Fri, 9 Nov 2018 14:14:04 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=134.134.136.24; helo=mga09.intel.com; envelope-from=dave.jiang@intel.com; receiver=linux-nvdimm@lists.01.org Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id ED8C421184E66 for ; Fri, 9 Nov 2018 14:14:02 -0800 (PST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga006.fm.intel.com ([10.253.24.20]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Nov 2018 14:14:02 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,484,1534834800"; d="scan'208";a="279853444" Received: from djiang5-desk3.ch.intel.com ([143.182.136.93]) by fmsmga006.fm.intel.com with ESMTP; 09 Nov 2018 14:14:02 -0800 Subject: [PATCH 03/11] libnvdimm/security: add override module param for key self verification From: Dave Jiang To: dan.j.williams@intel.com, zohar@linux.vnet.ibm.com Date: Fri, 09 Nov 2018 15:14:02 -0700 Message-ID: <154180164225.70506.5284765160410580627.stgit@djiang5-desk3.ch.intel.com> In-Reply-To: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> References: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-nvdimm@lists.01.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Provide the user an override via kernel module parameter for security key self verification. no_key_self_verify parameter is being added to bypass security key verify against the hardware during nvdimm unlock path. Signed-off-by: Dave Jiang --- drivers/nvdimm/security.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c index ee741199d623..d2831e61f3d8 100644 --- a/drivers/nvdimm/security.c +++ b/drivers/nvdimm/security.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2018 Intel Corporation. All rights reserved. */ +#include #include #include #include @@ -14,6 +15,10 @@ #include "nd-core.h" #include "nd.h" +static bool no_key_self_verify; +module_param(no_key_self_verify, bool, 0644); +MODULE_PARM_DESC(no_key_self_verify, "Bypass security key self verify"); + /* * Retrieve user injected key */ @@ -235,6 +240,12 @@ int nvdimm_security_unlock_dimm(struct nvdimm *nvdimm) * other security operations. */ if (nvdimm->state == NVDIMM_SECURITY_UNLOCKED) { + /* bypass if user override */ + if (no_key_self_verify) { + mutex_unlock(&nvdimm->sec_mutex); + return 0; + } + key = nvdimm_self_verify_key(nvdimm); if (!key) { rc = nvdimm_security_freeze_lock(nvdimm); From patchwork Fri Nov 9 22:14:07 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Jiang X-Patchwork-Id: 10676627 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1B4A213AD for ; Fri, 9 Nov 2018 22:14:10 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0C4812F1A5 for ; Fri, 9 Nov 2018 22:14:10 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0088D2F317; Fri, 9 Nov 2018 22:14:09 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 8823C2F1A5 for ; Fri, 9 Nov 2018 22:14:09 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 7AB082118EF64; Fri, 9 Nov 2018 14:14:09 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.136; helo=mga12.intel.com; envelope-from=dave.jiang@intel.com; receiver=linux-nvdimm@lists.01.org Received: from mga12.intel.com (mga12.intel.com [192.55.52.136]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 5093021184E66 for ; Fri, 9 Nov 2018 14:14:08 -0800 (PST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Nov 2018 14:14:07 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,484,1534834800"; d="scan'208";a="103114611" Received: from djiang5-desk3.ch.intel.com ([143.182.136.93]) by fmsmga002.fm.intel.com with ESMTP; 09 Nov 2018 14:14:07 -0800 Subject: [PATCH 04/11] libnvdimm/security: introduce NDD_SECURITY_BUSY flag From: Dave Jiang To: dan.j.williams@intel.com, zohar@linux.vnet.ibm.com Date: Fri, 09 Nov 2018 15:14:07 -0700 Message-ID: <154180164758.70506.11595335862050814723.stgit@djiang5-desk3.ch.intel.com> In-Reply-To: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> References: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-nvdimm@lists.01.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Adding a flag for nvdimm->flags to support erase functions. While it's ok to hold the nvdimm_bus lock for secure erase due to minimal time to execute the command, overwrite requires a significantly longer time and makes this impossible. The flag will block any drivers from being loaded and DIMMs being probed. Signed-off-by: Dave Jiang --- drivers/nvdimm/nd.h | 1 + drivers/nvdimm/region_devs.c | 7 +++++ drivers/nvdimm/security.c | 61 ++++++++++++++++++++++++++++++++++++++---- include/linux/libnvdimm.h | 2 + 4 files changed, 65 insertions(+), 6 deletions(-) diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index f8d8f0a2a40d..6cb1cd4a39d0 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -250,6 +250,7 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, void nvdimm_set_aliasing(struct device *dev); void nvdimm_set_locked(struct device *dev); void nvdimm_clear_locked(struct device *dev); +int nvdimm_check_security_busy(struct nvdimm *nvdimm); struct nd_btt *to_nd_btt(struct device *dev); struct nd_gen_sb { diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 174a418cb171..a097282b2c01 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -78,6 +78,13 @@ int nd_region_activate(struct nd_region *nd_region) for (i = 0; i < nd_region->ndr_mappings; i++) { struct nd_mapping *nd_mapping = &nd_region->mapping[i]; struct nvdimm *nvdimm = nd_mapping->nvdimm; + int rc; + + rc = nvdimm_check_security_busy(nvdimm); + if (rc) { + nvdimm_bus_unlock(&nd_region->dev); + return rc; + } /* at least one null hint slot per-dimm for the "no-hint" case */ flush_data_size += sizeof(void *); diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c index d2831e61f3d8..2a83be47798e 100644 --- a/drivers/nvdimm/security.c +++ b/drivers/nvdimm/security.c @@ -19,6 +19,27 @@ static bool no_key_self_verify; module_param(no_key_self_verify, bool, 0644); MODULE_PARM_DESC(no_key_self_verify, "Bypass security key self verify"); +/* + * Check if we are doing security wipes + */ +int nvdimm_check_security_busy(struct nvdimm *nvdimm) +{ + if (test_bit(NDD_SECURITY_BUSY, &nvdimm->flags)) + return -EBUSY; + + return 0; +} + +static inline void nvdimm_set_security_busy(struct nvdimm *nvdimm) +{ + set_bit(NDD_SECURITY_BUSY, &nvdimm->flags); +} + +static inline void nvdimm_clear_security_busy(struct nvdimm *nvdimm) +{ + clear_bit(NDD_SECURITY_BUSY, &nvdimm->flags); +} + /* * Retrieve user injected key */ @@ -85,6 +106,13 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid) nvdimm_bus_lock(dev); mutex_lock(&nvdimm->sec_mutex); + rc = nvdimm_check_security_busy(nvdimm); + if (rc < 0) { + dev_warn(dev, "Security operation in progress.\n"); + goto out; + } + + nvdimm_set_security_busy(nvdimm); if (atomic_read(&nvdimm->busy)) { dev_warn(dev, "Unable to secure erase while DIMM active.\n"); rc = -EBUSY; @@ -113,6 +141,7 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid) key_put(key); out: + nvdimm_clear_security_busy(nvdimm); mutex_unlock(&nvdimm->sec_mutex); nvdimm_bus_unlock(dev); nvdimm_security_get_state(nvdimm); @@ -130,15 +159,19 @@ int nvdimm_security_freeze_lock(struct nvdimm *nvdimm) return -EOPNOTSUPP; mutex_lock(&nvdimm->sec_mutex); + rc = nvdimm_check_security_busy(nvdimm); + if (rc < 0) + goto out; + rc = nvdimm->security_ops->freeze_lock(nvdimm); - if (rc < 0) { - mutex_unlock(&nvdimm->sec_mutex); - return rc; - } + if (rc < 0) + goto out; nvdimm_security_get_state(nvdimm); + +out: mutex_unlock(&nvdimm->sec_mutex); - return 0; + return rc; } int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid) @@ -156,6 +189,12 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid) return -EOPNOTSUPP; mutex_lock(&nvdimm->sec_mutex); + rc = nvdimm_check_security_busy(nvdimm); + if (rc < 0) { + mutex_unlock(&nvdimm->sec_mutex); + return rc; + } + /* look for a key from cached key */ key = nvdimm_lookup_user_key(dev, keyid); if (!key) { @@ -232,6 +271,12 @@ int nvdimm_security_unlock_dimm(struct nvdimm *nvdimm) return 0; mutex_lock(&nvdimm->sec_mutex); + rc = nvdimm_check_security_busy(nvdimm); + if (rc < 0) { + mutex_unlock(&nvdimm->sec_mutex); + return rc; + } + /* * If the pre-OS has unlocked the DIMM, we will attempt to send * the key from request_key() to the hardware for verification. @@ -294,7 +339,7 @@ int nvdimm_security_change_key(struct nvdimm *nvdimm, unsigned int old_keyid, unsigned int new_keyid) { int rc; - struct key *key, *old_key; + struct key *key = NULL, *old_key = NULL; void *old_data = NULL, *new_data; struct device *dev = &nvdimm->dev; struct encrypted_key_payload *epayload, *old_epayload; @@ -306,6 +351,10 @@ int nvdimm_security_change_key(struct nvdimm *nvdimm, return -EBUSY; mutex_lock(&nvdimm->sec_mutex); + rc = nvdimm_check_security_busy(nvdimm); + if (rc < 0) + goto out; + /* look for a key from cached key if exists */ old_key = nvdimm_lookup_user_key(dev, old_keyid); if (old_key) diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 1071fe12081b..f3941836b93d 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -38,6 +38,8 @@ enum { NDD_UNARMED = 1, /* locked memory devices should not be accessed */ NDD_LOCKED = 2, + /* memory under security wipes should not be accessed */ + NDD_SECURITY_BUSY = 3, /* need to set a limit somewhere, but yes, this is likely overkill */ ND_IOCTL_MAX_BUFLEN = SZ_4M, From patchwork Fri Nov 9 22:14:13 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Jiang X-Patchwork-Id: 10676629 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8C58413AD for ; Fri, 9 Nov 2018 22:14:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7D0222F1A5 for ; Fri, 9 Nov 2018 22:14:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 718922F317; Fri, 9 Nov 2018 22:14:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id A2C1F2F1A5 for ; Fri, 9 Nov 2018 22:14:15 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 9A2582118EF69; Fri, 9 Nov 2018 14:14:15 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=134.134.136.65; helo=mga03.intel.com; envelope-from=dave.jiang@intel.com; receiver=linux-nvdimm@lists.01.org Received: from mga03.intel.com (mga03.intel.com [134.134.136.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 2B7FA2118EF5C for ; Fri, 9 Nov 2018 14:14:14 -0800 (PST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga004.fm.intel.com ([10.253.24.48]) by orsmga103.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Nov 2018 14:14:13 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,484,1534834800"; d="scan'208";a="104989367" Received: from djiang5-desk3.ch.intel.com ([143.182.136.93]) by fmsmga004.fm.intel.com with ESMTP; 09 Nov 2018 14:14:13 -0800 Subject: [PATCH 05/11] acpi/nfit, libnvdimm/security: Add security DSM overwrite support From: Dave Jiang To: dan.j.williams@intel.com, zohar@linux.vnet.ibm.com Date: Fri, 09 Nov 2018 15:14:13 -0700 Message-ID: <154180165306.70506.12390921144378942420.stgit@djiang5-desk3.ch.intel.com> In-Reply-To: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> References: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-nvdimm@lists.01.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP We are adding support for the security calls of ovewrite and query overwrite from Intel DSM spec v1.7. This will allow triggering of overwrite on Intel NVDIMMs. The overwrite operation can take tens of minutes. When the overwrite DSM is issued successfully, the NVDIMMs will be unaccessible. The kernel will do backoff polling to detect when the overwrite process is completed. According to the DSM spec v1.7, the 128G NVDIMMs can take up to 15mins to perform overwrite and larger DIMMs will take longer. Signed-off-by: Dave Jiang --- drivers/acpi/nfit/intel.c | 113 +++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/nfit/intel.h | 3 + drivers/acpi/nfit/nfit.h | 1 drivers/nvdimm/core.c | 3 + drivers/nvdimm/dimm_devs.c | 22 ++++++++ drivers/nvdimm/nd-core.h | 4 ++ drivers/nvdimm/nd.h | 8 +++ drivers/nvdimm/security.c | 116 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/libnvdimm.h | 4 ++ 9 files changed, 273 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c index f8f63f483859..f6d2b217f1c8 100644 --- a/drivers/acpi/nfit/intel.c +++ b/drivers/acpi/nfit/intel.c @@ -20,6 +20,112 @@ #include /* for wbinvd_on_all_cpus() on !SMP builds */ +static int intel_dimm_security_query_overwrite(struct nvdimm *nvdimm) +{ + int cmd_rc, rc = 0; + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_query_overwrite cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_QUERY_OVERWRITE, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = 0, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + .cmd = { + .status = 0, + }, + }; + + if (!test_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &nfit_mem->dsm_mask)) + return -ENOTTY; + + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), &cmd_rc); + if (rc < 0) + goto out; + if (cmd_rc < 0) { + rc = cmd_rc; + goto out; + } + + switch (nd_cmd.cmd.status) { + case 0: + break; + case ND_INTEL_STATUS_OQUERY_INPROGRESS: + rc = -EBUSY; + goto out; + default: + rc = -ENXIO; + goto out; + } + + /* flush all cache before we make the nvdimms available */ + wbinvd_on_all_cpus(); + nfit_mem->overwrite = false; + +out: + return rc; +} + +static int intel_dimm_security_overwrite(struct nvdimm *nvdimm, + const struct nvdimm_key_data *nkey) +{ + int cmd_rc, rc = 0; + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_overwrite cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_OVERWRITE, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + .cmd = { + .status = 0, + }, + }; + + if (!test_bit(NVDIMM_INTEL_OVERWRITE, &nfit_mem->dsm_mask)) + return -ENOTTY; + + /* flush all cache before we erase DIMM */ + wbinvd_on_all_cpus(); + memcpy(nd_cmd.cmd.passphrase, nkey->data, + sizeof(nd_cmd.cmd.passphrase)); + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), &cmd_rc); + if (rc < 0) + goto out; + if (cmd_rc < 0) { + rc = cmd_rc; + goto out; + } + + switch (nd_cmd.cmd.status) { + case 0: + nfit_mem->overwrite = true; + break; + case ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED: + rc = -ENOTSUPP; + break; + case ND_INTEL_STATUS_INVALID_PASS: + rc = -EINVAL; + goto out; + case ND_INTEL_STATUS_INVALID_STATE: + default: + rc = -ENXIO; + goto out; + } + + out: + return rc; +} + static int intel_dimm_security_erase(struct nvdimm *nvdimm, const struct nvdimm_key_data *nkey) { @@ -319,6 +425,11 @@ static int intel_dimm_security_state(struct nvdimm *nvdimm, return 0; } + if (nfit_mem->overwrite == true) { + *state = NVDIMM_SECURITY_OVERWRITE; + return 0; + } + *state = NVDIMM_SECURITY_DISABLED; rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), &cmd_rc); if (rc < 0) @@ -367,5 +478,7 @@ static const struct nvdimm_security_ops __intel_security_ops = { .disable = intel_dimm_security_disable, .freeze_lock = intel_dimm_security_freeze_lock, .erase = intel_dimm_security_erase, + .overwrite = intel_dimm_security_overwrite, + .query_overwrite = intel_dimm_security_query_overwrite, }; const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops; diff --git a/drivers/acpi/nfit/intel.h b/drivers/acpi/nfit/intel.h index 0af5b6fd0aea..a4e01de966dc 100644 --- a/drivers/acpi/nfit/intel.h +++ b/drivers/acpi/nfit/intel.h @@ -50,12 +50,15 @@ extern const struct nvdimm_security_ops *intel_security_ops; #define ND_INTEL_STATUS_NOT_READY 9 #define ND_INTEL_STATUS_INVALID_STATE 10 #define ND_INTEL_STATUS_INVALID_PASS 11 +#define ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED 0x10007 +#define ND_INTEL_STATUS_OQUERY_INPROGRESS 0x10007 #define ND_INTEL_SEC_STATE_ENABLED 0x02 #define ND_INTEL_SEC_STATE_LOCKED 0x04 #define ND_INTEL_SEC_STATE_FROZEN 0x08 #define ND_INTEL_SEC_STATE_PLIMIT 0x10 #define ND_INTEL_SEC_STATE_UNSUPPORTED 0x20 +#define ND_INTEL_SEC_STATE_OVERWRITE 0x40 struct nd_intel_get_security_state { u32 status; diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index 00de13bc5714..d50dbb4cdeaf 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -204,6 +204,7 @@ struct nfit_mem { unsigned long flags; u32 dirty_shutdown; int family; + bool overwrite; }; struct acpi_nfit_desc { diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index acce050856a8..b2496c06178b 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -437,6 +437,9 @@ static __init int libnvdimm_init(void) { int rc; + rc = nvdimm_devs_init(); + if (rc) + return rc; rc = nvdimm_bus_init(); if (rc) return rc; diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 269f401a5e68..b613c131bfb9 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -25,6 +25,7 @@ #include "nd.h" static DEFINE_IDA(dimm_ida); +struct workqueue_struct *nvdimm_wq; /* * Retrieve bus and dimm handle and return if this bus supports @@ -386,6 +387,8 @@ __weak ssize_t security_show(struct device *dev, return sprintf(buf, "locked\n"); case NVDIMM_SECURITY_FROZEN: return sprintf(buf, "frozen\n"); + case NVDIMM_SECURITY_OVERWRITE: + return sprintf(buf, "overwrite\n"); case NVDIMM_SECURITY_UNSUPPORTED: default: return sprintf(buf, "unsupported\n"); @@ -432,6 +435,11 @@ static ssize_t security_store(struct device *dev, return -EINVAL; dev_dbg(dev, "erase %#x\n", old_key); rc = nvdimm_security_erase(nvdimm, old_key); + } else if (sysfs_streq(cmd, "overwrite")) { + if (rc != 2) + return -EINVAL; + dev_dbg(dev, "overwrite %u\n", old_key); + rc = nvdimm_security_overwrite(nvdimm, old_key); } else return -EINVAL; @@ -492,6 +500,8 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_dat dev->type = &nvdimm_device_type; dev->devt = MKDEV(nvdimm_major, nvdimm->id); dev->groups = groups; + nvdimm->overwrite_tmo = 0; + INIT_DELAYED_WORK(&nvdimm->dwork, nvdimm_overwrite_query); nd_device_register(dev); return nvdimm; @@ -768,7 +778,17 @@ int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count) } EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count); -void __exit nvdimm_devs_exit(void) +int __init nvdimm_devs_init(void) +{ + nvdimm_wq = create_singlethread_workqueue("nvdimm"); + if (!nvdimm_wq) + return -ENOMEM; + + return 0; +} + +void nvdimm_devs_exit(void) { + destroy_workqueue(nvdimm_wq); ida_destroy(&dimm_ida); } diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index cfb87f71db8d..20a8216c503d 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -22,6 +22,7 @@ extern struct list_head nvdimm_bus_list; extern struct mutex nvdimm_bus_list_mutex; extern int nvdimm_major; +extern struct workqueue_struct *nvdimm_wq; struct nvdimm_bus { struct nvdimm_bus_descriptor *nd_desc; @@ -46,6 +47,8 @@ struct nvdimm { const struct nvdimm_security_ops *security_ops; enum nvdimm_security_state state; struct mutex sec_mutex; + struct delayed_work dwork; + unsigned int overwrite_tmo; }; /** @@ -81,6 +84,7 @@ struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev); int __init nvdimm_bus_init(void); void nvdimm_bus_exit(void); void nvdimm_devs_exit(void); +int nvdimm_devs_init(void); void nd_region_devs_exit(void); void nd_region_probe_success(struct nvdimm_bus *nvdimm_bus, struct device *dev); struct nd_region; diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 6cb1cd4a39d0..ba41582c4cc2 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -435,6 +435,9 @@ int nvdimm_security_change_key(struct nvdimm *nvdimm, unsigned int old_keyid, int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid); int nvdimm_security_freeze_lock(struct nvdimm *nvdimm); int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid); +int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid); +void nvdimm_overwrite_query(struct work_struct *work); + #else static inline int nvdimm_security_unlock_dimm(struct nvdimm *nvdimm) { @@ -468,5 +471,10 @@ static inline int nvdimm_security_erase(struct nvdimm *nvdimm, { return -EOPNOTSUPP; } +int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid) +{ + return -EOPNOTSUPP; +} +void nvdimm_overwrite_query(struct work_struct *work) {} #endif #endif /* __ND_H__ */ diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c index 2a83be47798e..725acd211114 100644 --- a/drivers/nvdimm/security.c +++ b/drivers/nvdimm/security.c @@ -93,6 +93,122 @@ int nvdimm_security_get_state(struct nvdimm *nvdimm) return nvdimm->security_ops->state(nvdimm, &nvdimm->state); } +void nvdimm_overwrite_query(struct work_struct *work) +{ + struct nvdimm *nvdimm; + struct nvdimm_bus *nvdimm_bus; + int rc; + unsigned int tmo; + + nvdimm = container_of(work, typeof(*nvdimm), dwork.work); + nvdimm_bus = walk_to_nvdimm_bus(&nvdimm->dev); + tmo = nvdimm->overwrite_tmo; + + if (!nvdimm->security_ops) + return; + + rc = nvdimm->security_ops->query_overwrite(nvdimm); + if (rc == -EBUSY) { + + /* setup delayed work again */ + tmo += 10; + queue_delayed_work(nvdimm_wq, &nvdimm->dwork, tmo * HZ); + nvdimm->overwrite_tmo = min(15U * 60U, tmo); + return; + } + + if (rc < 0) + dev_warn(&nvdimm->dev, "Overwrite failed\n"); + else + dev_info(&nvdimm->dev, "Overwrite completed\n"); + + nvdimm->overwrite_tmo = 0; + nvdimm_clear_security_busy(nvdimm); + nvdimm_security_get_state(nvdimm); +} + +int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid) +{ + struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nvdimm->dev); + struct device *dev = &nvdimm->dev; + struct key *key = NULL; + struct encrypted_key_payload *epayload; + void *data; + int rc; + char pass[NVDIMM_PASSPHRASE_LEN]; + + if (!nvdimm->security_ops) + return 0; + + nvdimm_bus_lock(&nvdimm_bus->dev); + mutex_lock(&nvdimm->sec_mutex); + rc = nvdimm_check_security_busy(nvdimm); + if (rc) + goto out; + + nvdimm_set_security_busy(nvdimm); + + if (atomic_read(&nvdimm->busy)) { + dev_warn(dev, "Unable to overwrite while DIMM active.\n"); + nvdimm_clear_security_busy(nvdimm); + rc = -EBUSY; + goto out; + } + + if (dev_get_drvdata(dev)) { + dev_warn(dev, "Unable to overwrite while DIMM active.\n"); + nvdimm_clear_security_busy(nvdimm); + rc = -EINVAL; + goto out; + } + + if (nvdimm->state == NVDIMM_SECURITY_UNSUPPORTED) { + dev_warn(dev, "Attempt to overwrite in wrong state.\n"); + nvdimm_clear_security_busy(nvdimm); + rc = -EOPNOTSUPP; + goto out; + } + + key = nvdimm_lookup_user_key(dev, keyid); + if (!key && keyid != 0) { + dev_dbg(dev, "Unable to get and verify key\n"); + nvdimm_clear_security_busy(nvdimm); + rc = -ENOKEY; + goto out; + } + + if (key) { + down_read(&key->sem); + epayload = dereference_key_locked(key); + data = epayload->decrypted_data; + memcpy(pass, data, NVDIMM_PASSPHRASE_LEN); + up_read(&key->sem); + } else + memset(pass, 0, NVDIMM_PASSPHRASE_LEN); + /* + * Overwrite can execute w/o passphrase. we will let the hardware + * determine whether we need a passphrase or not. + */ + rc = nvdimm->security_ops->overwrite(nvdimm, + (const struct nvdimm_key_data *)pass); + if (rc == 0) { + /* clear out security data */ + if (key) + memset(pass, 0, NVDIMM_PASSPHRASE_LEN); + nvdimm->state = NVDIMM_SECURITY_OVERWRITE; + queue_delayed_work(nvdimm_wq, &nvdimm->dwork, 0); + } else + nvdimm_clear_security_busy(nvdimm); + + out: + key_put(key); + mutex_unlock(&nvdimm->sec_mutex); + nvdimm_bus_unlock(&nvdimm_bus->dev); + nvdimm_security_get_state(nvdimm); + return rc; + +} + int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid) { int rc = 0; diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index f3941836b93d..479421ce62c0 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -163,6 +163,7 @@ enum nvdimm_security_state { NVDIMM_SECURITY_UNLOCKED, NVDIMM_SECURITY_LOCKED, NVDIMM_SECURITY_FROZEN, + NVDIMM_SECURITY_OVERWRITE, NVDIMM_SECURITY_UNSUPPORTED, }; @@ -186,6 +187,9 @@ struct nvdimm_security_ops { int (*freeze_lock)(struct nvdimm *nvdimm); int (*erase)(struct nvdimm *nvdimm, const struct nvdimm_key_data *nkey); + int (*overwrite)(struct nvdimm *nvdimm, + const struct nvdimm_key_data *nkey); + int (*query_overwrite)(struct nvdimm *nvdimm); }; void badrange_init(struct badrange *badrange); From patchwork Fri Nov 9 22:14:18 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Jiang X-Patchwork-Id: 10676639 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B4A4F13AD for ; Fri, 9 Nov 2018 22:14:34 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A50B62F1A5 for ; Fri, 9 Nov 2018 22:14:34 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 98E0A2F317; Fri, 9 Nov 2018 22:14:34 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 3DD1D2F1A5 for ; Fri, 9 Nov 2018 22:14:33 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id E54A32118EF6D; Fri, 9 Nov 2018 14:14:33 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.88; helo=mga01.intel.com; envelope-from=dave.jiang@intel.com; receiver=linux-nvdimm@lists.01.org Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id E07452118EF5C for ; Fri, 9 Nov 2018 14:14:32 -0800 (PST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga004.fm.intel.com ([10.253.24.48]) by fmsmga101.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Nov 2018 14:14:18 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,484,1534834800"; d="scan'208";a="104989400" Received: from djiang5-desk3.ch.intel.com ([143.182.136.93]) by fmsmga004.fm.intel.com with ESMTP; 09 Nov 2018 14:14:18 -0800 Subject: [PATCH 06/11] tools/testing/nvdimm: Add overwrite support for nfit_test From: Dave Jiang To: dan.j.williams@intel.com, zohar@linux.vnet.ibm.com Date: Fri, 09 Nov 2018 15:14:18 -0700 Message-ID: <154180165842.70506.2177330092992336547.stgit@djiang5-desk3.ch.intel.com> In-Reply-To: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> References: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-nvdimm@lists.01.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP With the implementation of Intel NVDIMM DSM overwrite, we are adding unit test to nfit_test for testing of overwrite operation. Signed-off-by: Dave Jiang --- drivers/acpi/nfit/intel.h | 1 + tools/testing/nvdimm/dimm_devs.c | 2 + tools/testing/nvdimm/test/nfit.c | 55 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/drivers/acpi/nfit/intel.h b/drivers/acpi/nfit/intel.h index a4e01de966dc..0140d9b6a0b7 100644 --- a/drivers/acpi/nfit/intel.h +++ b/drivers/acpi/nfit/intel.h @@ -52,6 +52,7 @@ extern const struct nvdimm_security_ops *intel_security_ops; #define ND_INTEL_STATUS_INVALID_PASS 11 #define ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED 0x10007 #define ND_INTEL_STATUS_OQUERY_INPROGRESS 0x10007 +#define ND_INTEL_STATUS_OQUERY_SEQUENCE_ERR 0x20007 #define ND_INTEL_SEC_STATE_ENABLED 0x02 #define ND_INTEL_SEC_STATE_LOCKED 0x04 diff --git a/tools/testing/nvdimm/dimm_devs.c b/tools/testing/nvdimm/dimm_devs.c index de3a22988dc5..3f8ad3d445d2 100644 --- a/tools/testing/nvdimm/dimm_devs.c +++ b/tools/testing/nvdimm/dimm_devs.c @@ -29,6 +29,8 @@ ssize_t security_show(struct device *dev, return sprintf(buf, "locked\n"); case NVDIMM_SECURITY_FROZEN: return sprintf(buf, "frozen\n"); + case NVDIMM_SECURITY_OVERWRITE: + return sprintf(buf, "overwrite\n"); case NVDIMM_SECURITY_UNSUPPORTED: default: return sprintf(buf, "unsupported\n"); diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index e4b560494e0a..c885fe136f42 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -149,6 +149,7 @@ static int dimm_fail_cmd_code[NUM_DCR]; struct nfit_test_sec { u8 state; u8 passphrase[32]; + u64 overwrite_end_time; } dimm_sec_info[NUM_DCR]; static const struct nd_intel_smart smart_def = { @@ -1073,6 +1074,50 @@ static int nd_intel_test_cmd_secure_erase(struct nfit_test *t, return 0; } +static int nd_intel_test_cmd_overwrite(struct nfit_test *t, + struct nd_intel_overwrite *nd_cmd, + unsigned int buf_len, int dimm) +{ + struct device *dev = &t->pdev.dev; + struct nfit_test_sec *sec = &dimm_sec_info[dimm]; + + if ((sec->state & ND_INTEL_SEC_STATE_ENABLED) && + memcmp(nd_cmd->passphrase, sec->passphrase, + ND_INTEL_PASSPHRASE_SIZE) != 0) { + nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; + dev_dbg(dev, "overwrite: wrong passphrase\n"); + return 0; + } + + memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); + sec->state = ND_INTEL_SEC_STATE_OVERWRITE; + dev_dbg(dev, "overwrite progressing.\n"); + sec->overwrite_end_time = get_jiffies_64() + 5 * HZ; + + return 0; +} + +static int nd_intel_test_cmd_query_overwrite(struct nfit_test *t, + struct nd_intel_query_overwrite *nd_cmd, + unsigned int buf_len, int dimm) +{ + struct device *dev = &t->pdev.dev; + struct nfit_test_sec *sec = &dimm_sec_info[dimm]; + + if (!(sec->state & ND_INTEL_SEC_STATE_OVERWRITE)) { + nd_cmd->status = ND_INTEL_STATUS_OQUERY_SEQUENCE_ERR; + return 0; + } + + if (time_is_before_jiffies64(sec->overwrite_end_time)) { + sec->overwrite_end_time = 0; + sec->state = 0; + dev_dbg(dev, "overwrite is complete\n"); + } else + nd_cmd->status = ND_INTEL_STATUS_OQUERY_INPROGRESS; + return 0; +} + static int get_dimm(struct nfit_mem *nfit_mem, unsigned int func) { int i; @@ -1144,6 +1189,14 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, rc = nd_intel_test_cmd_secure_erase(t, buf, buf_len, i); break; + case NVDIMM_INTEL_OVERWRITE: + rc = nd_intel_test_cmd_overwrite(t, + buf, buf_len, i - t->dcr_idx); + break; + case NVDIMM_INTEL_QUERY_OVERWRITE: + rc = nd_intel_test_cmd_query_overwrite(t, + buf, buf_len, i - t->dcr_idx); + break; case ND_INTEL_ENABLE_LSS_STATUS: rc = nd_intel_test_cmd_set_lss_status(t, buf, buf_len); @@ -2379,6 +2432,8 @@ static void nfit_test0_setup(struct nfit_test *t) set_bit(NVDIMM_INTEL_UNLOCK_UNIT, &acpi_desc->dimm_cmd_force_en); set_bit(NVDIMM_INTEL_FREEZE_LOCK, &acpi_desc->dimm_cmd_force_en); set_bit(NVDIMM_INTEL_SECURE_ERASE, &acpi_desc->dimm_cmd_force_en); + set_bit(NVDIMM_INTEL_OVERWRITE, &acpi_desc->dimm_cmd_force_en); + set_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &acpi_desc->dimm_cmd_force_en); } static void nfit_test1_setup(struct nfit_test *t) From patchwork Fri Nov 9 22:14:23 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Jiang X-Patchwork-Id: 10676635 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 37CA1139B for ; Fri, 9 Nov 2018 22:14:26 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 281122F1A5 for ; Fri, 9 Nov 2018 22:14:26 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1C1FF2F317; Fri, 9 Nov 2018 22:14:26 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id BDD362F1A5 for ; Fri, 9 Nov 2018 22:14:25 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id B47DF2118EF66; Fri, 9 Nov 2018 14:14:25 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.93; helo=mga11.intel.com; envelope-from=dave.jiang@intel.com; receiver=linux-nvdimm@lists.01.org Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 58F5E21184E66 for ; Fri, 9 Nov 2018 14:14:24 -0800 (PST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Nov 2018 14:14:24 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,484,1534834800"; d="scan'208";a="278596818" Received: from djiang5-desk3.ch.intel.com ([143.182.136.93]) by fmsmga005.fm.intel.com with ESMTP; 09 Nov 2018 14:14:23 -0800 Subject: [PATCH 07/11] libnvdimm/security: add overwrite status notification From: Dave Jiang To: dan.j.williams@intel.com, zohar@linux.vnet.ibm.com Date: Fri, 09 Nov 2018 15:14:23 -0700 Message-ID: <154180166375.70506.1085533195452285285.stgit@djiang5-desk3.ch.intel.com> In-Reply-To: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> References: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-nvdimm@lists.01.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Adding sysfs notification for when overwrite has completed to allow user monitoring app to be aware of overwrite completion status. Signed-off-by: Dave Jiang --- drivers/acpi/nfit/core.c | 5 +++++ drivers/nvdimm/dimm_devs.c | 10 ++++++++++ drivers/nvdimm/nd-core.h | 1 + drivers/nvdimm/security.c | 2 ++ include/linux/libnvdimm.h | 1 + 5 files changed, 19 insertions(+) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index de4e00059277..3e6c7b653872 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -2033,6 +2033,11 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) if (!nvdimm) continue; + rc = nvdimm_setup_security_events(nvdimm); + if (rc < 0) + dev_warn(acpi_desc->dev, + "no security event setup failed\n"); + nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit"); if (nfit_kernfs) nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs, diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index b613c131bfb9..39e40074b5df 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -508,6 +508,16 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_dat } EXPORT_SYMBOL_GPL(__nvdimm_create); +int nvdimm_setup_security_events(struct nvdimm *nvdimm) +{ + nvdimm->overwrite_state = sysfs_get_dirent(nvdimm->dev.kobj.sd, + "security"); + if (!nvdimm->overwrite_state) + return -ENODEV; + return 0; +} +EXPORT_SYMBOL_GPL(nvdimm_setup_security_events); + int alias_dpa_busy(struct device *dev, void *data) { resource_size_t map_end, blk_start, new; diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index 20a8216c503d..b96e1b10e3eb 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -49,6 +49,7 @@ struct nvdimm { struct mutex sec_mutex; struct delayed_work dwork; unsigned int overwrite_tmo; + struct kernfs_node *overwrite_state; }; /** diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c index 725acd211114..f5ba633545b7 100644 --- a/drivers/nvdimm/security.c +++ b/drivers/nvdimm/security.c @@ -122,6 +122,8 @@ void nvdimm_overwrite_query(struct work_struct *work) else dev_info(&nvdimm->dev, "Overwrite completed\n"); + if (nvdimm->overwrite_state) + sysfs_notify_dirent(nvdimm->overwrite_state); nvdimm->overwrite_tmo = 0; nvdimm_clear_security_busy(nvdimm); nvdimm_security_get_state(nvdimm); diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 479421ce62c0..c3c5a1c6b1b7 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -227,6 +227,7 @@ static inline struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, cmd_mask, num_flush, flush_wpq, NULL, NULL); } +int nvdimm_setup_security_events(struct nvdimm *nvdimm); const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd); const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd); u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd, From patchwork Fri Nov 9 22:14:29 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Jiang X-Patchwork-Id: 10676637 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5D5DD139B for ; Fri, 9 Nov 2018 22:14:31 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4C7222F2CD for ; Fri, 9 Nov 2018 22:14:31 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3F6E52F1A5; Fri, 9 Nov 2018 22:14:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id DAF792F1A5 for ; Fri, 9 Nov 2018 22:14:30 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id CC59B21A07A92; Fri, 9 Nov 2018 14:14:30 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.93; helo=mga11.intel.com; envelope-from=dave.jiang@intel.com; receiver=linux-nvdimm@lists.01.org Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id A0BDB21184E66 for ; Fri, 9 Nov 2018 14:14:29 -0800 (PST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Nov 2018 14:14:29 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,484,1534834800"; d="scan'208";a="278596826" Received: from djiang5-desk3.ch.intel.com ([143.182.136.93]) by fmsmga005.fm.intel.com with ESMTP; 09 Nov 2018 14:14:29 -0800 Subject: [PATCH 08/11] libnvdimm/security: add documentation for ovewrite From: Dave Jiang To: dan.j.williams@intel.com, zohar@linux.vnet.ibm.com Date: Fri, 09 Nov 2018 15:14:29 -0700 Message-ID: <154180166906.70506.2262123031486305806.stgit@djiang5-desk3.ch.intel.com> In-Reply-To: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> References: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-nvdimm@lists.01.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Add overwrite command usages to security documentation. Signed-off-by: Dave Jiang --- Documentation/nvdimm/security.txt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Documentation/nvdimm/security.txt b/Documentation/nvdimm/security.txt index 11240ce48755..dfe70a8fa25b 100644 --- a/Documentation/nvdimm/security.txt +++ b/Documentation/nvdimm/security.txt @@ -96,9 +96,19 @@ its keyid should be passed in via sysfs. The command format for doing a secure erase is: erase -An "old" key with the passphrase payload that is tied to the nvdimm should be -injected with a key description that does not have the "nvdimm:" prefix and -its keyid should be passed in via sysfs. +9. Overwrite +------------ +The command format for doing an overwrite is: +overwrite + +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 key passphrase that is tied to the nvdimm +should be injected and its keyid should be passed in via sysfs. [1]: http://pmem.io/documents/NVDIMM_DSM_Interface-V1.7.pdf [2]: http://www.t13.org/documents/UploadedDocuments/docs2006/e05179r4-ACS-SecurityClarifications.pdf From patchwork Fri Nov 9 22:14:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Jiang X-Patchwork-Id: 10676643 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2AF6F139B for ; Fri, 9 Nov 2018 22:14:37 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 173612F1A5 for ; Fri, 9 Nov 2018 22:14:37 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0B5412F317; Fri, 9 Nov 2018 22:14:37 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 18FA12F1A5 for ; Fri, 9 Nov 2018 22:14:36 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 0BF6B2118EF73; Fri, 9 Nov 2018 14:14:36 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.115; helo=mga14.intel.com; envelope-from=dave.jiang@intel.com; receiver=linux-nvdimm@lists.01.org Received: from mga14.intel.com (mga14.intel.com [192.55.52.115]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 8652E2118EF65 for ; Fri, 9 Nov 2018 14:14:35 -0800 (PST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga103.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Nov 2018 14:14:35 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,484,1534834800"; d="scan'208";a="89961839" Received: from djiang5-desk3.ch.intel.com ([143.182.136.93]) by orsmga006.jf.intel.com with ESMTP; 09 Nov 2018 14:14:34 -0800 Subject: [PATCH 09/11] acpi/nfit, libnvdimm/security: add Intel DSM 1.8 master passphrase support From: Dave Jiang To: dan.j.williams@intel.com, zohar@linux.vnet.ibm.com Date: Fri, 09 Nov 2018 15:14:34 -0700 Message-ID: <154180167436.70506.13449520468879463615.stgit@djiang5-desk3.ch.intel.com> In-Reply-To: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> References: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-nvdimm@lists.01.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP With Intel DSM 1.8 [1] two new security DSMs are introduced. Enable/update master passphrase and master secure erase. The master passphrase allows a secure erase to be performed without the user passphrase that is set on the NVDIMM. The commands of master_update and master_erase are added to the sysfs knob in order to initiate the DSMs. They are similar in opeartion mechanism compare to update and erase. [1]: http://pmem.io/documents/NVDIMM_DSM_Interface-V1.8.pdf Signed-off-by: Dave Jiang --- Documentation/nvdimm/security.txt | 23 ++++++ drivers/acpi/nfit/core.c | 2 + drivers/acpi/nfit/intel.c | 132 ++++++++++++++++++++++++++++++++++++- drivers/acpi/nfit/intel.h | 18 +++++ drivers/acpi/nfit/nfit.h | 6 +- drivers/nvdimm/dimm_devs.c | 16 ++++ drivers/nvdimm/nd-core.h | 1 drivers/nvdimm/nd.h | 5 + drivers/nvdimm/security.c | 34 ++++++++-- include/linux/libnvdimm.h | 15 ++++ 10 files changed, 236 insertions(+), 16 deletions(-) diff --git a/Documentation/nvdimm/security.txt b/Documentation/nvdimm/security.txt index dfe70a8fa25b..a2d954349417 100644 --- a/Documentation/nvdimm/security.txt +++ b/Documentation/nvdimm/security.txt @@ -110,5 +110,28 @@ Overwrite can last tens of minutes or more depending on nvdimm size. An encrypted key with the current key 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 + +The operating mechansim 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 + +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.7.pdf [2]: http://www.t13.org/documents/UploadedDocuments/docs2006/e05179r4-ACS-SecurityClarifications.pdf diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 3e6c7b653872..867e6fea3737 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -389,6 +389,8 @@ static u8 nfit_dsm_revid(unsigned family, unsigned func) [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; diff --git a/drivers/acpi/nfit/intel.c b/drivers/acpi/nfit/intel.c index f6d2b217f1c8..4e47fc2b524c 100644 --- a/drivers/acpi/nfit/intel.c +++ b/drivers/acpi/nfit/intel.c @@ -20,6 +20,123 @@ #include /* for wbinvd_on_all_cpus() on !SMP builds */ +static int intel_dimm_security_master_erase(struct nvdimm *nvdimm, + const struct nvdimm_key_data *nkey) +{ + int cmd_rc, rc = 0; + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_secure_erase cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_MASTER_SECURE_ERASE, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = ND_INTEL_PASSPHRASE_SIZE, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + .cmd = { + .status = 0, + }, + }; + + if (!test_bit(NVDIMM_INTEL_MASTER_SECURE_ERASE, &nfit_mem->dsm_mask)) + return -ENOTTY; + + /* flush all cache before we erase DIMM */ + wbinvd_on_all_cpus(); + memcpy(nd_cmd.cmd.passphrase, nkey->data, + sizeof(nd_cmd.cmd.passphrase)); + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), &cmd_rc); + if (rc < 0) + goto out; + if (cmd_rc < 0) { + rc = cmd_rc; + goto out; + } + + switch (nd_cmd.cmd.status) { + case 0: + break; + case ND_INTEL_STATUS_NOT_SUPPORTED: + rc = -EOPNOTSUPP; + goto out; + case ND_INTEL_STATUS_INVALID_PASS: + rc = -EINVAL; + goto out; + case ND_INTEL_STATUS_INVALID_STATE: + default: + rc = -ENXIO; + goto out; + } + + /* DIMM erased, invalidate all CPU caches before we read it */ + wbinvd_on_all_cpus(); + + out: + return rc; +} + +static int intel_dimm_security_master_update_passphrase(struct nvdimm *nvdimm, + const struct nvdimm_key_data *old_data, + const struct nvdimm_key_data *new_data) +{ + int cmd_rc, rc = 0; + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + struct { + struct nd_cmd_pkg pkg; + struct nd_intel_set_passphrase cmd; + } nd_cmd = { + .pkg = { + .nd_command = NVDIMM_INTEL_SET_MASTER_PASSPHRASE, + .nd_family = NVDIMM_FAMILY_INTEL, + .nd_size_in = ND_INTEL_PASSPHRASE_SIZE * 2, + .nd_size_out = ND_INTEL_STATUS_SIZE, + .nd_fw_size = ND_INTEL_STATUS_SIZE, + }, + .cmd = { + .status = 0, + }, + }; + + if (!test_bit(NVDIMM_INTEL_SET_MASTER_PASSPHRASE, + &nfit_mem->dsm_mask)) + return -ENOTTY; + + if (old_data) + memcpy(nd_cmd.cmd.old_pass, old_data->data, + sizeof(nd_cmd.cmd.old_pass)); + else + memset(nd_cmd.cmd.old_pass, 0, sizeof(nd_cmd.cmd.old_pass)); + memcpy(nd_cmd.cmd.new_pass, new_data->data, + sizeof(nd_cmd.cmd.new_pass)); + rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), &cmd_rc); + if (rc < 0) + goto out; + if (cmd_rc < 0) { + rc = cmd_rc; + goto out; + } + + switch (nd_cmd.cmd.status) { + case 0: + break; + case ND_INTEL_STATUS_NOT_SUPPORTED: + rc = -EOPNOTSUPP; + goto out; + case ND_INTEL_STATUS_INVALID_PASS: + rc = -EINVAL; + goto out; + case ND_INTEL_STATUS_INVALID_STATE: + default: + rc = -ENXIO; + goto out; + } + + out: + return rc; +} static int intel_dimm_security_query_overwrite(struct nvdimm *nvdimm) { int cmd_rc, rc = 0; @@ -397,7 +514,8 @@ static int intel_dimm_security_unlock(struct nvdimm *nvdimm, } static int intel_dimm_security_state(struct nvdimm *nvdimm, - enum nvdimm_security_state *state) + enum nvdimm_security_state *state, + enum nvdimm_security_extended_state *ext_state) { int cmd_rc, rc = 0; struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); @@ -465,9 +583,17 @@ static int intel_dimm_security_state(struct nvdimm *nvdimm, } else *state = NVDIMM_SECURITY_DISABLED; + *ext_state = NVDIMM_SECURITY_MASTER_DISABLED; + if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_ENABLED) + *ext_state = NVDIMM_SECURITY_MASTER_ENABLED; + else if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_PLIMIT) + *ext_state = NVDIMM_SECURITY_MASTER_FROZEN; + out: - if (rc < 0) + if (rc < 0) { *state = NVDIMM_SECURITY_INVALID; + *ext_state = NVDIMM_SECURITY_MASTER_INVALID; + } return rc; } @@ -480,5 +606,7 @@ static const struct nvdimm_security_ops __intel_security_ops = { .erase = intel_dimm_security_erase, .overwrite = intel_dimm_security_overwrite, .query_overwrite = intel_dimm_security_query_overwrite, + .master_change_key = intel_dimm_security_master_update_passphrase, + .master_erase = intel_dimm_security_master_erase, }; const struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops; diff --git a/drivers/acpi/nfit/intel.h b/drivers/acpi/nfit/intel.h index 0140d9b6a0b7..0c33f49d0272 100644 --- a/drivers/acpi/nfit/intel.h +++ b/drivers/acpi/nfit/intel.h @@ -46,6 +46,7 @@ extern const struct nvdimm_security_ops *intel_security_ops; #define ND_INTEL_STATUS_SIZE 4 #define ND_INTEL_PASSPHRASE_SIZE 32 +#define ND_INTEL_STATUS_NOT_SUPPORTED 1 #define ND_INTEL_STATUS_RETRY 5 #define ND_INTEL_STATUS_NOT_READY 9 #define ND_INTEL_STATUS_INVALID_STATE 10 @@ -61,9 +62,13 @@ extern const struct nvdimm_security_ops *intel_security_ops; #define ND_INTEL_SEC_STATE_UNSUPPORTED 0x20 #define ND_INTEL_SEC_STATE_OVERWRITE 0x40 +#define ND_INTEL_SEC_ESTATE_ENABLED 0x01 +#define ND_INTEL_SEC_ESTATE_PLIMIT 0x02 + struct nd_intel_get_security_state { u32 status; - u32 reserved; + u8 extended_state; + u8 reserved[3]; u8 state; u8 reserved1[3]; } __packed; @@ -101,6 +106,17 @@ struct nd_intel_overwrite { struct nd_intel_query_overwrite { u32 status; } __packed; + +struct nd_intel_set_master_passphrase { + u8 old_pass[ND_INTEL_PASSPHRASE_SIZE]; + u8 new_pass[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; + +struct nd_intel_master_secure_erase { + u8 passphrase[ND_INTEL_PASSPHRASE_SIZE]; + u32 status; +} __packed; #else /* CONFIG_X86 */ #define intel_security_ops (NULL) #endif /* CONFIG_X86 */ diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index d50dbb4cdeaf..e0ee54049c89 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -68,13 +68,17 @@ enum nvdimm_family_cmds { NVDIMM_INTEL_SECURE_ERASE = 24, NVDIMM_INTEL_OVERWRITE = 25, NVDIMM_INTEL_QUERY_OVERWRITE = 26, + NVDIMM_INTEL_SET_MASTER_PASSPHRASE = 27, + NVDIMM_INTEL_MASTER_SECURE_ERASE = 28, }; #define NVDIMM_INTEL_SECURITY_CMDMASK \ (1 << NVDIMM_INTEL_GET_SECURITY_STATE | 1 << NVDIMM_INTEL_SET_PASSPHRASE \ | 1 << NVDIMM_INTEL_DISABLE_PASSPHRASE | 1 << NVDIMM_INTEL_UNLOCK_UNIT \ | 1 << NVDIMM_INTEL_FREEZE_LOCK | 1 << NVDIMM_INTEL_SECURE_ERASE \ -| 1 << NVDIMM_INTEL_OVERWRITE | 1 << NVDIMM_INTEL_QUERY_OVERWRITE) +| 1 << NVDIMM_INTEL_OVERWRITE | 1 << NVDIMM_INTEL_QUERY_OVERWRITE \ +| 1 << NVDIMM_INTEL_SET_MASTER_PASSPHRASE \ +| 1 << NVDIMM_INTEL_MASTER_SECURE_ERASE) #define NVDIMM_INTEL_CMDMASK \ (NVDIMM_STANDARD_CMDMASK | 1 << NVDIMM_INTEL_GET_MODES \ diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 39e40074b5df..fdb32c30d01d 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -421,7 +421,8 @@ static ssize_t security_store(struct device *dev, if (rc != 3) return -EINVAL; dev_dbg(dev, "update %#x %#x\n", old_key, new_key); - rc = nvdimm_security_change_key(nvdimm, old_key, new_key); + rc = nvdimm_security_change_key(nvdimm, old_key, new_key, + false); } else if (sysfs_streq(cmd, "disable")) { if (rc != 2) return -EINVAL; @@ -434,12 +435,23 @@ static ssize_t security_store(struct device *dev, if (rc != 2) return -EINVAL; dev_dbg(dev, "erase %#x\n", old_key); - rc = nvdimm_security_erase(nvdimm, old_key); + rc = nvdimm_security_erase(nvdimm, old_key, false); } else if (sysfs_streq(cmd, "overwrite")) { if (rc != 2) return -EINVAL; dev_dbg(dev, "overwrite %u\n", old_key); rc = nvdimm_security_overwrite(nvdimm, old_key); + } else if (sysfs_streq(cmd, "master_update")) { + if (rc != 3) + return -EINVAL; + dev_dbg(dev, "master_update %#x %#x\n", old_key, new_key); + rc = nvdimm_security_change_key(nvdimm, + old_key, new_key, true); + } else if (sysfs_streq(cmd, "master_erase")) { + if (rc != 2) + return -EINVAL; + dev_dbg(dev, "master_erase %#x\n", old_key); + rc = nvdimm_security_erase(nvdimm, old_key, true); } else return -EINVAL; diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index b96e1b10e3eb..98facc66dd88 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -46,6 +46,7 @@ struct nvdimm { const char *dimm_id; const struct nvdimm_security_ops *security_ops; enum nvdimm_security_state state; + enum nvdimm_security_extended_state ext_state; struct mutex sec_mutex; struct delayed_work dwork; unsigned int overwrite_tmo; diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index ba41582c4cc2..11a268ffa63d 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -431,10 +431,11 @@ bool pmem_should_map_pages(struct device *dev); int nvdimm_security_unlock_dimm(struct nvdimm *nvdimm); int nvdimm_security_get_state(struct nvdimm *nvdimm); int nvdimm_security_change_key(struct nvdimm *nvdimm, unsigned int old_keyid, - unsigned int new_keyid); + unsigned int new_keyid, bool master); int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid); int nvdimm_security_freeze_lock(struct nvdimm *nvdimm); -int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid); +int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid, + bool master); int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid); void nvdimm_overwrite_query(struct work_struct *work); diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c index f5ba633545b7..2e835e6a7b74 100644 --- a/drivers/nvdimm/security.c +++ b/drivers/nvdimm/security.c @@ -90,7 +90,8 @@ int nvdimm_security_get_state(struct nvdimm *nvdimm) if (!nvdimm->security_ops) return 0; - return nvdimm->security_ops->state(nvdimm, &nvdimm->state); + return nvdimm->security_ops->state(nvdimm, &nvdimm->state, + &nvdimm->ext_state); } void nvdimm_overwrite_query(struct work_struct *work) @@ -211,7 +212,8 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid) } -int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid) +int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid, + bool master) { int rc = 0; struct key *key; @@ -243,6 +245,12 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid) goto out; } + if (nvdimm->ext_state != NVDIMM_SECURITY_MASTER_ENABLED && master) { + dev_warn(dev, "Attempt to secure erase in wrong master state.\n"); + rc = -EOPNOTSUPP; + goto out; + } + key = nvdimm_lookup_user_key(dev, keyid); if (!key) { dev_dbg(dev, "Unable to get and verify key\n"); @@ -253,8 +261,12 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid) down_read(&key->sem); epayload = dereference_key_locked(key); data = epayload->decrypted_data; - rc = nvdimm->security_ops->erase(nvdimm, - (const struct nvdimm_key_data *)data); + if (master) + rc = nvdimm->security_ops->master_erase(nvdimm, + (const struct nvdimm_key_data *)data); + else + rc = nvdimm->security_ops->erase(nvdimm, + (const struct nvdimm_key_data *)data); up_read(&key->sem); key_put(key); @@ -454,7 +466,7 @@ int nvdimm_security_unlock_dimm(struct nvdimm *nvdimm) } int nvdimm_security_change_key(struct nvdimm *nvdimm, - unsigned int old_keyid, unsigned int new_keyid) + unsigned int old_keyid, unsigned int new_keyid, bool master) { int rc; struct key *key = NULL, *old_key = NULL; @@ -468,6 +480,10 @@ int nvdimm_security_change_key(struct nvdimm *nvdimm, if (nvdimm->state == NVDIMM_SECURITY_FROZEN) return -EBUSY; + /* can only change master passphrase when security is disabled */ + if (nvdimm->ext_state != NVDIMM_SECURITY_MASTER_ENABLED && master) + return -EOPNOTSUPP; + mutex_lock(&nvdimm->sec_mutex); rc = nvdimm_check_security_busy(nvdimm); if (rc < 0) @@ -499,8 +515,12 @@ int nvdimm_security_change_key(struct nvdimm *nvdimm, old_data = old_epayload->decrypted_data; } - rc = nvdimm->security_ops->change_key(nvdimm, old_data, - new_data); + if (master) + rc = nvdimm->security_ops->master_change_key(nvdimm, old_data, + new_data); + else + rc = nvdimm->security_ops->change_key(nvdimm, old_data, + new_data); if (rc) dev_warn(dev, "key update failed: %d\n", rc); diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index c3c5a1c6b1b7..3d14f73c7bca 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -167,6 +167,13 @@ enum nvdimm_security_state { NVDIMM_SECURITY_UNSUPPORTED, }; +enum nvdimm_security_extended_state { + NVDIMM_SECURITY_MASTER_INVALID = 0, + NVDIMM_SECURITY_MASTER_DISABLED, + NVDIMM_SECURITY_MASTER_ENABLED, + NVDIMM_SECURITY_MASTER_FROZEN, +}; + #define NVDIMM_PASSPHRASE_LEN 32 #define NVDIMM_KEY_DESC_LEN 22 @@ -176,7 +183,8 @@ struct nvdimm_key_data { struct nvdimm_security_ops { int (*state)(struct nvdimm *nvdimm, - enum nvdimm_security_state *state); + enum nvdimm_security_state *state, + enum nvdimm_security_extended_state *ex_state); int (*unlock)(struct nvdimm *nvdimm, const struct nvdimm_key_data *nkey); int (*change_key)(struct nvdimm *nvdimm, @@ -190,6 +198,11 @@ struct nvdimm_security_ops { int (*overwrite)(struct nvdimm *nvdimm, const struct nvdimm_key_data *nkey); int (*query_overwrite)(struct nvdimm *nvdimm); + int (*master_change_key)(struct nvdimm *nvdimm, + const struct nvdimm_key_data *old_key, + const struct nvdimm_key_data *new_key); + int (*master_erase)(struct nvdimm *nvdimm, + const struct nvdimm_key_data *nkey); }; void badrange_init(struct badrange *badrange); From patchwork Fri Nov 9 22:14:39 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Jiang X-Patchwork-Id: 10676645 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id AE1BE139B for ; Fri, 9 Nov 2018 22:14:42 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A06852F1A5 for ; Fri, 9 Nov 2018 22:14:42 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 944402F317; Fri, 9 Nov 2018 22:14:42 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 2B8D82F1A5 for ; Fri, 9 Nov 2018 22:14:42 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 20A0F2118EF5C; Fri, 9 Nov 2018 14:14:42 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=134.134.136.24; helo=mga09.intel.com; envelope-from=dave.jiang@intel.com; receiver=linux-nvdimm@lists.01.org Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id BA7E52118EF5C for ; Fri, 9 Nov 2018 14:14:40 -0800 (PST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Nov 2018 14:14:40 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,484,1534834800"; d="scan'208";a="99062571" Received: from djiang5-desk3.ch.intel.com ([143.182.136.93]) by orsmga003.jf.intel.com with ESMTP; 09 Nov 2018 14:14:40 -0800 Subject: [PATCH 10/11] tools/testing/nvdimm: add Intel DSM 1.8 support for nfit_test From: Dave Jiang To: dan.j.williams@intel.com, zohar@linux.vnet.ibm.com Date: Fri, 09 Nov 2018 15:14:39 -0700 Message-ID: <154180167993.70506.16150690843165035447.stgit@djiang5-desk3.ch.intel.com> In-Reply-To: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> References: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-nvdimm@lists.01.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Adding test support for new Intel DSM from v1.8. The ability of simulating master passphrase update and master secure erase have been added to nfit_test. Signed-off-by: Dave Jiang --- tools/testing/nvdimm/test/nfit.c | 86 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index c885fe136f42..602f6703614e 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -148,7 +148,9 @@ static unsigned long dimm_fail_cmd_flags[NUM_DCR]; static int dimm_fail_cmd_code[NUM_DCR]; struct nfit_test_sec { u8 state; + u8 ext_state; u8 passphrase[32]; + u8 master_passphrase[32]; u64 overwrite_end_time; } dimm_sec_info[NUM_DCR]; @@ -951,6 +953,7 @@ static int nd_intel_test_cmd_security_status(struct nfit_test *t, nd_cmd->status = 0; nd_cmd->state = sec->state; + nd_cmd->extended_state = sec->ext_state; dev_dbg(dev, "security state (%#x) returned\n", nd_cmd->state); return 0; @@ -1067,7 +1070,9 @@ static int nd_intel_test_cmd_secure_erase(struct nfit_test *t, dev_dbg(dev, "secure erase: wrong passphrase\n"); } else { memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); + memset(sec->master_passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); sec->state = 0; + sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; dev_dbg(dev, "secure erase: done\n"); } @@ -1112,12 +1117,69 @@ static int nd_intel_test_cmd_query_overwrite(struct nfit_test *t, if (time_is_before_jiffies64(sec->overwrite_end_time)) { sec->overwrite_end_time = 0; sec->state = 0; + sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; dev_dbg(dev, "overwrite is complete\n"); } else nd_cmd->status = ND_INTEL_STATUS_OQUERY_INPROGRESS; return 0; } +static int nd_intel_test_cmd_master_set_pass(struct nfit_test *t, + struct nd_intel_set_master_passphrase *nd_cmd, + unsigned int buf_len, int dimm) +{ + struct device *dev = &t->pdev.dev; + struct nfit_test_sec *sec = &dimm_sec_info[dimm]; + + if (!(sec->ext_state & ND_INTEL_SEC_ESTATE_ENABLED)) { + nd_cmd->status = ND_INTEL_STATUS_NOT_SUPPORTED; + dev_dbg(dev, "master set passphrase in wrong state\n"); + } else if (sec->ext_state & ND_INTEL_SEC_ESTATE_PLIMIT) { + nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; + dev_dbg(dev, "master set passphrase in wrong security state\n"); + } else if (memcmp(nd_cmd->old_pass, sec->master_passphrase, + ND_INTEL_PASSPHRASE_SIZE) != 0) { + nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; + dev_dbg(dev, "master set passphrase wrong passphrase\n"); + } else { + memcpy(sec->master_passphrase, nd_cmd->new_pass, + ND_INTEL_PASSPHRASE_SIZE); + nd_cmd->status = 0; + dev_dbg(dev, "master passphrase updated\n"); + } + + return 0; +} + +static int nd_intel_test_cmd_master_secure_erase(struct nfit_test *t, + struct nd_intel_master_secure_erase *nd_cmd, + unsigned int buf_len, int dimm) +{ + struct device *dev = &t->pdev.dev; + struct nfit_test_sec *sec = &dimm_sec_info[dimm]; + + if (!(sec->ext_state & ND_INTEL_SEC_ESTATE_ENABLED)) { + nd_cmd->status = ND_INTEL_STATUS_NOT_SUPPORTED; + dev_dbg(dev, "master erase in wrong state\n"); + } else if (sec->ext_state & ND_INTEL_SEC_ESTATE_PLIMIT) { + nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE; + dev_dbg(dev, "master erase in wrong security state\n"); + } else if (memcmp(nd_cmd->passphrase, sec->master_passphrase, + ND_INTEL_PASSPHRASE_SIZE) != 0) { + nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS; + dev_dbg(dev, "master secure erase: wrong passphrase\n"); + } else { + memset(sec->master_passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); + sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; + memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE); + sec->state = 0; + dev_dbg(dev, "master secure erase: done\n"); + } + + return 0; +} + + static int get_dimm(struct nfit_mem *nfit_mem, unsigned int func) { int i; @@ -1197,6 +1259,14 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, rc = nd_intel_test_cmd_query_overwrite(t, buf, buf_len, i - t->dcr_idx); break; + case NVDIMM_INTEL_SET_MASTER_PASSPHRASE: + rc = nd_intel_test_cmd_master_set_pass(t, + buf, buf_len, i); + break; + case NVDIMM_INTEL_MASTER_SECURE_ERASE: + rc = nd_intel_test_cmd_master_secure_erase(t, + buf, buf_len, i); + break; case ND_INTEL_ENABLE_LSS_STATUS: rc = nd_intel_test_cmd_set_lss_status(t, buf, buf_len); @@ -1575,6 +1645,17 @@ static int nfit_test_dimm_init(struct nfit_test *t) return 0; } +static void security_init(struct nfit_test *t) +{ + int i; + + for (i = 0; i < t->num_dcr; i++) { + struct nfit_test_sec *sec = &dimm_sec_info[i]; + + sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED; + } +} + static void smart_init(struct nfit_test *t) { int i; @@ -1653,6 +1734,7 @@ static int nfit_test0_alloc(struct nfit_test *t) if (nfit_test_dimm_init(t)) return -ENOMEM; smart_init(t); + security_init(t); return ars_state_init(&t->pdev.dev, &t->ars_state); } @@ -2434,6 +2516,10 @@ static void nfit_test0_setup(struct nfit_test *t) set_bit(NVDIMM_INTEL_SECURE_ERASE, &acpi_desc->dimm_cmd_force_en); set_bit(NVDIMM_INTEL_OVERWRITE, &acpi_desc->dimm_cmd_force_en); set_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &acpi_desc->dimm_cmd_force_en); + set_bit(NVDIMM_INTEL_SET_MASTER_PASSPHRASE, + &acpi_desc->dimm_cmd_force_en); + set_bit(NVDIMM_INTEL_MASTER_SECURE_ERASE, + &acpi_desc->dimm_cmd_force_en); } static void nfit_test1_setup(struct nfit_test *t) From patchwork Fri Nov 9 22:14:45 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Jiang X-Patchwork-Id: 10676647 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C212013AD for ; Fri, 9 Nov 2018 22:14:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B04652F1A5 for ; Fri, 9 Nov 2018 22:14:47 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A47592F317; Fri, 9 Nov 2018 22:14:47 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 433AB2F1A5 for ; Fri, 9 Nov 2018 22:14:47 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 3B6CE21A00AE6; Fri, 9 Nov 2018 14:14:47 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=134.134.136.24; helo=mga09.intel.com; envelope-from=dave.jiang@intel.com; receiver=linux-nvdimm@lists.01.org Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 2EB0F2118EF60 for ; Fri, 9 Nov 2018 14:14:46 -0800 (PST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga005.jf.intel.com ([10.7.209.41]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Nov 2018 14:14:45 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,484,1534834800"; d="scan'208";a="272832828" Received: from djiang5-desk3.ch.intel.com ([143.182.136.93]) by orsmga005.jf.intel.com with ESMTP; 09 Nov 2018 14:14:45 -0800 Subject: [PATCH 11/11] acpi/nfit: prevent indiscriminate DSM payload dumping for security DSMs From: Dave Jiang To: dan.j.williams@intel.com, zohar@linux.vnet.ibm.com Date: Fri, 09 Nov 2018 15:14:45 -0700 Message-ID: <154180168546.70506.10546818094914224030.stgit@djiang5-desk3.ch.intel.com> In-Reply-To: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> References: <154180093865.70506.6858789591063128903.stgit@djiang5-desk3.ch.intel.com> User-Agent: StGit/unknown-version MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-nvdimm@lists.01.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP Right now when debug is enabled, we dump the command buffer indescriminately. This exposes the clear text payload for security DSMs. Introducing a kernel config to only dump the payload if the config option is turned on so the production kernels can leave this option off and not expose the passphrases. Signed-off-by: Dave Jiang --- drivers/acpi/nfit/Kconfig | 7 +++++++ drivers/acpi/nfit/core.c | 24 +++++++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/nfit/Kconfig b/drivers/acpi/nfit/Kconfig index f7c57e33499e..a0a8eabda2e8 100644 --- a/drivers/acpi/nfit/Kconfig +++ b/drivers/acpi/nfit/Kconfig @@ -13,3 +13,10 @@ config ACPI_NFIT To compile this driver as a module, choose M here: the module will be called nfit. + +config NFIT_SECURITY_DEBUG + bool "Turn on debugging for NVDIMM security features" + depends on ACPI_NFIT + help + Turning on debug output for NVDIMM security DSM commands. This + should not be turned on on a production kernel. diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 867e6fea3737..baaf5308de35 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -405,6 +405,21 @@ static u8 nfit_dsm_revid(unsigned family, unsigned func) return id; } +static bool is_security_cmd(unsigned int cmd, unsigned int func, + unsigned int family) +{ + if (cmd != ND_CMD_CALL) + return false; + + if (family == NVDIMM_FAMILY_INTEL) { + if (func >= NVDIMM_INTEL_GET_SECURITY_STATE && + func <= NVDIMM_INTEL_MASTER_SECURE_ERASE) + return true; + } + + return false; +} + 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) { @@ -489,9 +504,12 @@ 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 ((call_pkg && !is_security_cmd(cmd, func, call_pkg->nd_family)) || + IS_ENABLED(CONFIG_NFIT_SECURITY_DEBUG)) { + 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