Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 03b65b22 authored by Dave Jiang's avatar Dave Jiang Committed by Dan Williams
Browse files

acpi/nfit, libnvdimm: Add disable passphrase support to Intel nvdimm.



Add support to disable passphrase (security) for the Intel nvdimm. The
passphrase used for disabling is pulled from an encrypted-key in the kernel
user keyring. The action is triggered by writing "disable <keyid>" to the
sysfs attribute "security".

Signed-off-by: default avatarDave Jiang <dave.jiang@intel.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent 4c6926a2
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -163,6 +163,46 @@ static int intel_security_unlock(struct nvdimm *nvdimm,
	return 0;
}

static int intel_security_disable(struct nvdimm *nvdimm,
		const struct nvdimm_key_data *key_data)
{
	int rc;
	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
	struct {
		struct nd_cmd_pkg pkg;
		struct nd_intel_disable_passphrase cmd;
	} nd_cmd = {
		.pkg = {
			.nd_command = NVDIMM_INTEL_DISABLE_PASSPHRASE,
			.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,
		},
	};

	if (!test_bit(NVDIMM_INTEL_DISABLE_PASSPHRASE, &nfit_mem->dsm_mask))
		return -ENOTTY;

	memcpy(nd_cmd.cmd.passphrase, key_data->data,
			sizeof(nd_cmd.cmd.passphrase));
	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
	if (rc < 0)
		return rc;

	switch (nd_cmd.cmd.status) {
	case 0:
		break;
	case ND_INTEL_STATUS_INVALID_PASS:
		return -EINVAL;
	case ND_INTEL_STATUS_INVALID_STATE:
	default:
		return -ENXIO;
	}

	return 0;
}

/*
 * TODO: define a cross arch wbinvd equivalent when/if
 * NVDIMM_FAMILY_INTEL command support arrives on another arch.
@@ -183,6 +223,7 @@ static const struct nvdimm_security_ops __intel_security_ops = {
	.state = intel_security_state,
	.freeze = intel_security_freeze,
	.change_key = intel_security_change_key,
	.disable = intel_security_disable,
#ifdef CONFIG_X86
	.unlock = intel_security_unlock,
#endif
+44 −3
Original line number Diff line number Diff line
@@ -391,24 +391,65 @@ static ssize_t security_show(struct device *dev,
	return -ENOTTY;
}

#define OPS						\
	C( OP_FREEZE,        "freeze",        1),	\
	C( OP_DISABLE,       "disable",       2)
#undef C
#define C(a, b, c) a
enum nvdimmsec_op_ids { OPS };
#undef C
#define C(a, b, c) { b, c }
static struct {
	const char *name;
	int args;
} ops[] = { OPS };
#undef C

#define SEC_CMD_SIZE 32
#define KEY_ID_SIZE 10

static ssize_t __security_store(struct device *dev, const char *buf, size_t len)
{
	struct nvdimm *nvdimm = to_nvdimm(dev);
	ssize_t rc;
	char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1],
		nkeystr[KEY_ID_SIZE+1];
	unsigned int key, newkey;
	int i;

	if (atomic_read(&nvdimm->busy))
		return -EBUSY;

	if (sysfs_streq(buf, "freeze")) {
	rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s"
			" %"__stringify(KEY_ID_SIZE)"s"
			" %"__stringify(KEY_ID_SIZE)"s",
			cmd, keystr, nkeystr);
	if (rc < 1)
		return -EINVAL;
	for (i = 0; i < ARRAY_SIZE(ops); i++)
		if (sysfs_streq(cmd, ops[i].name))
			break;
	if (i >= ARRAY_SIZE(ops))
		return -EINVAL;
	if (ops[i].args > 1)
		rc = kstrtouint(keystr, 0, &key);
	if (rc >= 0 && ops[i].args > 2)
		rc = kstrtouint(nkeystr, 0, &newkey);
	if (rc < 0)
		return rc;

	if (i == OP_FREEZE) {
		dev_dbg(dev, "freeze\n");
		rc = nvdimm_security_freeze(nvdimm);
	} else if (i == OP_DISABLE) {
		dev_dbg(dev, "disable %u\n", key);
		rc = nvdimm_security_disable(nvdimm, key);
	} else
		return -EINVAL;

	if (rc == 0)
		rc = len;
	return rc;

}

static ssize_t security_store(struct device *dev,
@@ -452,7 +493,7 @@ static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n)
	if (nvdimm->sec.state < 0)
		return 0;
	/* Are there any state mutation ops? */
	if (nvdimm->sec.ops->freeze)
	if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable)
		return a->mode;
	return 0444;
}
+9 −0
Original line number Diff line number Diff line
@@ -57,6 +57,15 @@ static inline enum nvdimm_security_state nvdimm_security_state(
	return nvdimm->sec.ops->state(nvdimm);
}
int nvdimm_security_freeze(struct nvdimm *nvdimm);
#if IS_ENABLED(CONFIG_NVDIMM_KEYS)
int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid);
#else
static inline int nvdimm_security_disable(struct nvdimm *nvdimm,
		unsigned int keyid)
{
	return -EOPNOTSUPP;
}
#endif

/**
 * struct blk_alloc_info - tracking info for BLK dpa scanning
+63 −0
Original line number Diff line number Diff line
@@ -69,6 +69,36 @@ static struct key *nvdimm_request_key(struct nvdimm *nvdimm)
	return key;
}

static struct key *nvdimm_lookup_user_key(struct nvdimm *nvdimm,
		key_serial_t id)
{
	key_ref_t keyref;
	struct key *key;
	struct encrypted_key_payload *epayload;
	struct device *dev = &nvdimm->dev;

	keyref = lookup_user_key(id, 0, 0);
	if (IS_ERR(keyref))
		return NULL;

	key = key_ref_to_ptr(keyref);
	if (key->type != &key_type_encrypted) {
		key_put(key);
		return NULL;
	}
	dev_dbg(dev, "%s: key found: %#x\n", __func__, key_serial(key));


	down_read(&key->sem);
	epayload = dereference_key_locked(key);
	if (epayload->decrypted_datalen != NVDIMM_PASSPHRASE_LEN) {
		up_read(&key->sem);
		key_put(key);
		key = NULL;
	}
	return key;
}

static struct key *nvdimm_key_revalidate(struct nvdimm *nvdimm)
{
	struct key *key;
@@ -146,3 +176,36 @@ int nvdimm_security_unlock(struct device *dev)
	nvdimm_bus_unlock(dev);
	return rc;
}

int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid)
{
	struct device *dev = &nvdimm->dev;
	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
	struct key *key;
	int rc;

	/* The bus lock should be held at the top level of the call stack */
	lockdep_assert_held(&nvdimm_bus->reconfig_mutex);

	if (!nvdimm->sec.ops || !nvdimm->sec.ops->disable
			|| nvdimm->sec.state < 0)
		return -EOPNOTSUPP;

	if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) {
		dev_warn(dev, "Incorrect security state: %d\n",
				nvdimm->sec.state);
		return -EIO;
	}

	key = nvdimm_lookup_user_key(nvdimm, keyid);
	if (!key)
		return -ENOKEY;

	rc = nvdimm->sec.ops->disable(nvdimm, key_data(key));
	dev_dbg(dev, "key: %d disable: %s\n", key_serial(key),
			rc == 0 ? "success" : "fail");

	nvdimm_put_key(key);
	nvdimm->sec.state = nvdimm_security_state(nvdimm);
	return rc;
}
+2 −0
Original line number Diff line number Diff line
@@ -178,6 +178,8 @@ struct nvdimm_security_ops {
			const struct nvdimm_key_data *new_data);
	int (*unlock)(struct nvdimm *nvdimm,
			const struct nvdimm_key_data *key_data);
	int (*disable)(struct nvdimm *nvdimm,
			const struct nvdimm_key_data *key_data);
};

void badrange_init(struct badrange *badrange);