File Index
Files and directories
Sign in
FILE

report.c

File preview with syntax highlighting.

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