brintos

brintos / linux-shallow public Read only

0
0
Text · 13.1 KiB · 9432d4e Raw
513 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* Copyright(c) 2023 Intel Corporation. All rights reserved. */3 4#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt5 6#include <linux/tsm.h>7#include <linux/err.h>8#include <linux/slab.h>9#include <linux/rwsem.h>10#include <linux/string.h>11#include <linux/module.h>12#include <linux/cleanup.h>13#include <linux/configfs.h>14 15static struct tsm_provider {16	const struct tsm_ops *ops;17	void *data;18} provider;19static DECLARE_RWSEM(tsm_rwsem);20 21/**22 * DOC: Trusted Security Module (TSM) Attestation Report Interface23 *24 * The TSM report interface is a common provider of blobs that facilitate25 * attestation of a TVM (confidential computing guest) by an attestation26 * service. A TSM report combines a user-defined blob (likely a public-key with27 * a nonce for a key-exchange protocol) with a signed attestation report. That28 * combined blob is then used to obtain secrets provided by an agent that can29 * validate the attestation report. The expectation is that this interface is30 * invoked infrequently, however configfs allows for multiple agents to31 * own their own report generation instances to generate reports as32 * often as needed.33 *34 * The attestation report format is TSM provider specific, when / if a standard35 * materializes that can be published instead of the vendor layout. Until then36 * the 'provider' attribute indicates the format of 'outblob', and optionally37 * 'auxblob' and 'manifestblob'.38 */39 40struct tsm_report_state {41	struct tsm_report report;42	unsigned long write_generation;43	unsigned long read_generation;44	struct config_item cfg;45};46 47enum tsm_data_select {48	TSM_REPORT,49	TSM_CERTS,50	TSM_MANIFEST,51};52 53static struct tsm_report *to_tsm_report(struct config_item *cfg)54{55	struct tsm_report_state *state =56		container_of(cfg, struct tsm_report_state, cfg);57 58	return &state->report;59}60 61static struct tsm_report_state *to_state(struct tsm_report *report)62{63	return container_of(report, struct tsm_report_state, report);64}65 66static int try_advance_write_generation(struct tsm_report *report)67{68	struct tsm_report_state *state = to_state(report);69 70	lockdep_assert_held_write(&tsm_rwsem);71 72	/*73	 * Malicious or broken userspace has written enough times for74	 * read_generation == write_generation by modular arithmetic without an75	 * interim read. Stop accepting updates until the current report76	 * configuration is read.77	 */78	if (state->write_generation == state->read_generation - 1)79		return -EBUSY;80	state->write_generation++;81	return 0;82}83 84static ssize_t tsm_report_privlevel_store(struct config_item *cfg,85					  const char *buf, size_t len)86{87	struct tsm_report *report = to_tsm_report(cfg);88	unsigned int val;89	int rc;90 91	rc = kstrtouint(buf, 0, &val);92	if (rc)93		return rc;94 95	/*96	 * The valid privilege levels that a TSM might accept, if it accepts a97	 * privilege level setting at all, are a max of TSM_PRIVLEVEL_MAX (see98	 * SEV-SNP GHCB) and a minimum of a TSM selected floor value no less99	 * than 0.100	 */101	if (provider.ops->privlevel_floor > val || val > TSM_PRIVLEVEL_MAX)102		return -EINVAL;103 104	guard(rwsem_write)(&tsm_rwsem);105	rc = try_advance_write_generation(report);106	if (rc)107		return rc;108	report->desc.privlevel = val;109 110	return len;111}112CONFIGFS_ATTR_WO(tsm_report_, privlevel);113 114static ssize_t tsm_report_privlevel_floor_show(struct config_item *cfg,115					       char *buf)116{117	guard(rwsem_read)(&tsm_rwsem);118	return sysfs_emit(buf, "%u\n", provider.ops->privlevel_floor);119}120CONFIGFS_ATTR_RO(tsm_report_, privlevel_floor);121 122static ssize_t tsm_report_service_provider_store(struct config_item *cfg,123						 const char *buf, size_t len)124{125	struct tsm_report *report = to_tsm_report(cfg);126	size_t sp_len;127	char *sp;128	int rc;129 130	guard(rwsem_write)(&tsm_rwsem);131	rc = try_advance_write_generation(report);132	if (rc)133		return rc;134 135	sp_len = (buf[len - 1] != '\n') ? len : len - 1;136 137	sp = kstrndup(buf, sp_len, GFP_KERNEL);138	if (!sp)139		return -ENOMEM;140	kfree(report->desc.service_provider);141 142	report->desc.service_provider = sp;143 144	return len;145}146CONFIGFS_ATTR_WO(tsm_report_, service_provider);147 148static ssize_t tsm_report_service_guid_store(struct config_item *cfg,149					     const char *buf, size_t len)150{151	struct tsm_report *report = to_tsm_report(cfg);152	int rc;153 154	guard(rwsem_write)(&tsm_rwsem);155	rc = try_advance_write_generation(report);156	if (rc)157		return rc;158 159	report->desc.service_guid = guid_null;160 161	rc = guid_parse(buf, &report->desc.service_guid);162	if (rc)163		return rc;164 165	return len;166}167CONFIGFS_ATTR_WO(tsm_report_, service_guid);168 169static ssize_t tsm_report_service_manifest_version_store(struct config_item *cfg,170							 const char *buf, size_t len)171{172	struct tsm_report *report = to_tsm_report(cfg);173	unsigned int val;174	int rc;175 176	rc = kstrtouint(buf, 0, &val);177	if (rc)178		return rc;179 180	guard(rwsem_write)(&tsm_rwsem);181	rc = try_advance_write_generation(report);182	if (rc)183		return rc;184	report->desc.service_manifest_version = val;185 186	return len;187}188CONFIGFS_ATTR_WO(tsm_report_, service_manifest_version);189 190static ssize_t tsm_report_inblob_write(struct config_item *cfg,191				       const void *buf, size_t count)192{193	struct tsm_report *report = to_tsm_report(cfg);194	int rc;195 196	guard(rwsem_write)(&tsm_rwsem);197	rc = try_advance_write_generation(report);198	if (rc)199		return rc;200 201	report->desc.inblob_len = count;202	memcpy(report->desc.inblob, buf, count);203	return count;204}205CONFIGFS_BIN_ATTR_WO(tsm_report_, inblob, NULL, TSM_INBLOB_MAX);206 207static ssize_t tsm_report_generation_show(struct config_item *cfg, char *buf)208{209	struct tsm_report *report = to_tsm_report(cfg);210	struct tsm_report_state *state = to_state(report);211 212	guard(rwsem_read)(&tsm_rwsem);213	return sysfs_emit(buf, "%lu\n", state->write_generation);214}215CONFIGFS_ATTR_RO(tsm_report_, generation);216 217static ssize_t tsm_report_provider_show(struct config_item *cfg, char *buf)218{219	guard(rwsem_read)(&tsm_rwsem);220	return sysfs_emit(buf, "%s\n", provider.ops->name);221}222CONFIGFS_ATTR_RO(tsm_report_, provider);223 224static ssize_t __read_report(struct tsm_report *report, void *buf, size_t count,225			     enum tsm_data_select select)226{227	loff_t offset = 0;228	ssize_t len;229	u8 *out;230 231	if (select == TSM_REPORT) {232		out = report->outblob;233		len = report->outblob_len;234	} else if (select == TSM_MANIFEST) {235		out = report->manifestblob;236		len = report->manifestblob_len;237	} else {238		out = report->auxblob;239		len = report->auxblob_len;240	}241 242	/*243	 * Recall that a NULL @buf is configfs requesting the size of244	 * the buffer.245	 */246	if (!buf)247		return len;248	return memory_read_from_buffer(buf, count, &offset, out, len);249}250 251static ssize_t read_cached_report(struct tsm_report *report, void *buf,252				  size_t count, enum tsm_data_select select)253{254	struct tsm_report_state *state = to_state(report);255 256	guard(rwsem_read)(&tsm_rwsem);257	if (!report->desc.inblob_len)258		return -EINVAL;259 260	/*261	 * A given TSM backend always fills in ->outblob regardless of262	 * whether the report includes an auxblob/manifestblob or not.263	 */264	if (!report->outblob ||265	    state->read_generation != state->write_generation)266		return -EWOULDBLOCK;267 268	return __read_report(report, buf, count, select);269}270 271static ssize_t tsm_report_read(struct tsm_report *report, void *buf,272			       size_t count, enum tsm_data_select select)273{274	struct tsm_report_state *state = to_state(report);275	const struct tsm_ops *ops;276	ssize_t rc;277 278	/* try to read from the existing report if present and valid... */279	rc = read_cached_report(report, buf, count, select);280	if (rc >= 0 || rc != -EWOULDBLOCK)281		return rc;282 283	/* slow path, report may need to be regenerated... */284	guard(rwsem_write)(&tsm_rwsem);285	ops = provider.ops;286	if (!ops)287		return -ENOTTY;288	if (!report->desc.inblob_len)289		return -EINVAL;290 291	/* did another thread already generate this report? */292	if (report->outblob &&293	    state->read_generation == state->write_generation)294		goto out;295 296	kvfree(report->outblob);297	kvfree(report->auxblob);298	kvfree(report->manifestblob);299	report->outblob = NULL;300	report->auxblob = NULL;301	report->manifestblob = NULL;302	rc = ops->report_new(report, provider.data);303	if (rc < 0)304		return rc;305	state->read_generation = state->write_generation;306out:307	return __read_report(report, buf, count, select);308}309 310static ssize_t tsm_report_outblob_read(struct config_item *cfg, void *buf,311				       size_t count)312{313	struct tsm_report *report = to_tsm_report(cfg);314 315	return tsm_report_read(report, buf, count, TSM_REPORT);316}317CONFIGFS_BIN_ATTR_RO(tsm_report_, outblob, NULL, TSM_OUTBLOB_MAX);318 319static ssize_t tsm_report_auxblob_read(struct config_item *cfg, void *buf,320				       size_t count)321{322	struct tsm_report *report = to_tsm_report(cfg);323 324	return tsm_report_read(report, buf, count, TSM_CERTS);325}326CONFIGFS_BIN_ATTR_RO(tsm_report_, auxblob, NULL, TSM_OUTBLOB_MAX);327 328static ssize_t tsm_report_manifestblob_read(struct config_item *cfg, void *buf,329					    size_t count)330{331	struct tsm_report *report = to_tsm_report(cfg);332 333	return tsm_report_read(report, buf, count, TSM_MANIFEST);334}335CONFIGFS_BIN_ATTR_RO(tsm_report_, manifestblob, NULL, TSM_OUTBLOB_MAX);336 337static struct configfs_attribute *tsm_report_attrs[] = {338	[TSM_REPORT_GENERATION] = &tsm_report_attr_generation,339	[TSM_REPORT_PROVIDER] = &tsm_report_attr_provider,340	[TSM_REPORT_PRIVLEVEL] = &tsm_report_attr_privlevel,341	[TSM_REPORT_PRIVLEVEL_FLOOR] = &tsm_report_attr_privlevel_floor,342	[TSM_REPORT_SERVICE_PROVIDER] = &tsm_report_attr_service_provider,343	[TSM_REPORT_SERVICE_GUID] = &tsm_report_attr_service_guid,344	[TSM_REPORT_SERVICE_MANIFEST_VER] = &tsm_report_attr_service_manifest_version,345	NULL,346};347 348static struct configfs_bin_attribute *tsm_report_bin_attrs[] = {349	[TSM_REPORT_INBLOB] = &tsm_report_attr_inblob,350	[TSM_REPORT_OUTBLOB] = &tsm_report_attr_outblob,351	[TSM_REPORT_AUXBLOB] = &tsm_report_attr_auxblob,352	[TSM_REPORT_MANIFESTBLOB] = &tsm_report_attr_manifestblob,353	NULL,354};355 356static void tsm_report_item_release(struct config_item *cfg)357{358	struct tsm_report *report = to_tsm_report(cfg);359	struct tsm_report_state *state = to_state(report);360 361	kvfree(report->manifestblob);362	kvfree(report->auxblob);363	kvfree(report->outblob);364	kfree(report->desc.service_provider);365	kfree(state);366}367 368static struct configfs_item_operations tsm_report_item_ops = {369	.release = tsm_report_item_release,370};371 372static bool tsm_report_is_visible(struct config_item *item,373				  struct configfs_attribute *attr, int n)374{375	guard(rwsem_read)(&tsm_rwsem);376	if (!provider.ops)377		return false;378 379	if (!provider.ops->report_attr_visible)380		return true;381 382	return provider.ops->report_attr_visible(n);383}384 385static bool tsm_report_is_bin_visible(struct config_item *item,386				      struct configfs_bin_attribute *attr, int n)387{388	guard(rwsem_read)(&tsm_rwsem);389	if (!provider.ops)390		return false;391 392	if (!provider.ops->report_bin_attr_visible)393		return true;394 395	return provider.ops->report_bin_attr_visible(n);396}397 398static struct configfs_group_operations tsm_report_attr_group_ops = {399	.is_visible = tsm_report_is_visible,400	.is_bin_visible = tsm_report_is_bin_visible,401};402 403static const struct config_item_type tsm_report_type = {404	.ct_owner = THIS_MODULE,405	.ct_bin_attrs = tsm_report_bin_attrs,406	.ct_attrs = tsm_report_attrs,407	.ct_item_ops = &tsm_report_item_ops,408	.ct_group_ops = &tsm_report_attr_group_ops,409};410 411static struct config_item *tsm_report_make_item(struct config_group *group,412						const char *name)413{414	struct tsm_report_state *state;415 416	guard(rwsem_read)(&tsm_rwsem);417	if (!provider.ops)418		return ERR_PTR(-ENXIO);419 420	state = kzalloc(sizeof(*state), GFP_KERNEL);421	if (!state)422		return ERR_PTR(-ENOMEM);423 424	config_item_init_type_name(&state->cfg, name, &tsm_report_type);425	return &state->cfg;426}427 428static struct configfs_group_operations tsm_report_group_ops = {429	.make_item = tsm_report_make_item,430};431 432static const struct config_item_type tsm_reports_type = {433	.ct_owner = THIS_MODULE,434	.ct_group_ops = &tsm_report_group_ops,435};436 437static const struct config_item_type tsm_root_group_type = {438	.ct_owner = THIS_MODULE,439};440 441static struct configfs_subsystem tsm_configfs = {442	.su_group = {443		.cg_item = {444			.ci_namebuf = "tsm",445			.ci_type = &tsm_root_group_type,446		},447	},448	.su_mutex = __MUTEX_INITIALIZER(tsm_configfs.su_mutex),449};450 451int tsm_register(const struct tsm_ops *ops, void *priv)452{453	const struct tsm_ops *conflict;454 455	guard(rwsem_write)(&tsm_rwsem);456	conflict = provider.ops;457	if (conflict) {458		pr_err("\"%s\" ops already registered\n", conflict->name);459		return -EBUSY;460	}461 462	provider.ops = ops;463	provider.data = priv;464	return 0;465}466EXPORT_SYMBOL_GPL(tsm_register);467 468int tsm_unregister(const struct tsm_ops *ops)469{470	guard(rwsem_write)(&tsm_rwsem);471	if (ops != provider.ops)472		return -EBUSY;473	provider.ops = NULL;474	provider.data = NULL;475	return 0;476}477EXPORT_SYMBOL_GPL(tsm_unregister);478 479static struct config_group *tsm_report_group;480 481static int __init tsm_init(void)482{483	struct config_group *root = &tsm_configfs.su_group;484	struct config_group *tsm;485	int rc;486 487	config_group_init(root);488	rc = configfs_register_subsystem(&tsm_configfs);489	if (rc)490		return rc;491 492	tsm = configfs_register_default_group(root, "report",493					      &tsm_reports_type);494	if (IS_ERR(tsm)) {495		configfs_unregister_subsystem(&tsm_configfs);496		return PTR_ERR(tsm);497	}498	tsm_report_group = tsm;499 500	return 0;501}502module_init(tsm_init);503 504static void __exit tsm_exit(void)505{506	configfs_unregister_default_group(tsm_report_group);507	configfs_unregister_subsystem(&tsm_configfs);508}509module_exit(tsm_exit);510 511MODULE_LICENSE("GPL");512MODULE_DESCRIPTION("Provide Trusted Security Module attestation reports via configfs");513