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