162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013 Politecnico di Torino, Italy 462306a36Sopenharmony_ci * TORSEC group -- https://security.polito.it 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Roberto Sassu <roberto.sassu@polito.it> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * File: ima_template.c 962306a36Sopenharmony_ci * Helpers to manage template descriptors. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/rculist.h> 1362306a36Sopenharmony_ci#include "ima.h" 1462306a36Sopenharmony_ci#include "ima_template_lib.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cienum header_fields { HDR_PCR, HDR_DIGEST, HDR_TEMPLATE_NAME, 1762306a36Sopenharmony_ci HDR_TEMPLATE_DATA, HDR__LAST }; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic struct ima_template_desc builtin_templates[] = { 2062306a36Sopenharmony_ci {.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT}, 2162306a36Sopenharmony_ci {.name = "ima-ng", .fmt = "d-ng|n-ng"}, 2262306a36Sopenharmony_ci {.name = "ima-sig", .fmt = "d-ng|n-ng|sig"}, 2362306a36Sopenharmony_ci {.name = "ima-ngv2", .fmt = "d-ngv2|n-ng"}, 2462306a36Sopenharmony_ci {.name = "ima-sigv2", .fmt = "d-ngv2|n-ng|sig"}, 2562306a36Sopenharmony_ci {.name = "ima-buf", .fmt = "d-ng|n-ng|buf"}, 2662306a36Sopenharmony_ci {.name = "ima-modsig", .fmt = "d-ng|n-ng|sig|d-modsig|modsig"}, 2762306a36Sopenharmony_ci {.name = "evm-sig", 2862306a36Sopenharmony_ci .fmt = "d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode"}, 2962306a36Sopenharmony_ci {.name = "", .fmt = ""}, /* placeholder for a custom format */ 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic LIST_HEAD(defined_templates); 3362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(template_list); 3462306a36Sopenharmony_cistatic int template_setup_done; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic const struct ima_template_field supported_fields[] = { 3762306a36Sopenharmony_ci {.field_id = "d", .field_init = ima_eventdigest_init, 3862306a36Sopenharmony_ci .field_show = ima_show_template_digest}, 3962306a36Sopenharmony_ci {.field_id = "n", .field_init = ima_eventname_init, 4062306a36Sopenharmony_ci .field_show = ima_show_template_string}, 4162306a36Sopenharmony_ci {.field_id = "d-ng", .field_init = ima_eventdigest_ng_init, 4262306a36Sopenharmony_ci .field_show = ima_show_template_digest_ng}, 4362306a36Sopenharmony_ci {.field_id = "d-ngv2", .field_init = ima_eventdigest_ngv2_init, 4462306a36Sopenharmony_ci .field_show = ima_show_template_digest_ngv2}, 4562306a36Sopenharmony_ci {.field_id = "n-ng", .field_init = ima_eventname_ng_init, 4662306a36Sopenharmony_ci .field_show = ima_show_template_string}, 4762306a36Sopenharmony_ci {.field_id = "sig", .field_init = ima_eventsig_init, 4862306a36Sopenharmony_ci .field_show = ima_show_template_sig}, 4962306a36Sopenharmony_ci {.field_id = "buf", .field_init = ima_eventbuf_init, 5062306a36Sopenharmony_ci .field_show = ima_show_template_buf}, 5162306a36Sopenharmony_ci {.field_id = "d-modsig", .field_init = ima_eventdigest_modsig_init, 5262306a36Sopenharmony_ci .field_show = ima_show_template_digest_ng}, 5362306a36Sopenharmony_ci {.field_id = "modsig", .field_init = ima_eventmodsig_init, 5462306a36Sopenharmony_ci .field_show = ima_show_template_sig}, 5562306a36Sopenharmony_ci {.field_id = "evmsig", .field_init = ima_eventevmsig_init, 5662306a36Sopenharmony_ci .field_show = ima_show_template_sig}, 5762306a36Sopenharmony_ci {.field_id = "iuid", .field_init = ima_eventinodeuid_init, 5862306a36Sopenharmony_ci .field_show = ima_show_template_uint}, 5962306a36Sopenharmony_ci {.field_id = "igid", .field_init = ima_eventinodegid_init, 6062306a36Sopenharmony_ci .field_show = ima_show_template_uint}, 6162306a36Sopenharmony_ci {.field_id = "imode", .field_init = ima_eventinodemode_init, 6262306a36Sopenharmony_ci .field_show = ima_show_template_uint}, 6362306a36Sopenharmony_ci {.field_id = "xattrnames", 6462306a36Sopenharmony_ci .field_init = ima_eventinodexattrnames_init, 6562306a36Sopenharmony_ci .field_show = ima_show_template_string}, 6662306a36Sopenharmony_ci {.field_id = "xattrlengths", 6762306a36Sopenharmony_ci .field_init = ima_eventinodexattrlengths_init, 6862306a36Sopenharmony_ci .field_show = ima_show_template_sig}, 6962306a36Sopenharmony_ci {.field_id = "xattrvalues", 7062306a36Sopenharmony_ci .field_init = ima_eventinodexattrvalues_init, 7162306a36Sopenharmony_ci .field_show = ima_show_template_sig}, 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * Used when restoring measurements carried over from a kexec. 'd' and 'n' don't 7662306a36Sopenharmony_ci * need to be accounted for since they shouldn't be defined in the same template 7762306a36Sopenharmony_ci * description as 'd-ng' and 'n-ng' respectively. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci#define MAX_TEMPLATE_NAME_LEN \ 8062306a36Sopenharmony_ci sizeof("d-ng|n-ng|evmsig|xattrnames|xattrlengths|xattrvalues|iuid|igid|imode") 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic struct ima_template_desc *ima_template; 8362306a36Sopenharmony_cistatic struct ima_template_desc *ima_buf_template; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/** 8662306a36Sopenharmony_ci * ima_template_has_modsig - Check whether template has modsig-related fields. 8762306a36Sopenharmony_ci * @ima_template: IMA template to check. 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * Tells whether the given template has fields referencing a file's appended 9062306a36Sopenharmony_ci * signature. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cibool ima_template_has_modsig(const struct ima_template_desc *ima_template) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci int i; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci for (i = 0; i < ima_template->num_fields; i++) 9762306a36Sopenharmony_ci if (!strcmp(ima_template->fields[i]->field_id, "modsig") || 9862306a36Sopenharmony_ci !strcmp(ima_template->fields[i]->field_id, "d-modsig")) 9962306a36Sopenharmony_ci return true; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return false; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int __init ima_template_setup(char *str) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct ima_template_desc *template_desc; 10762306a36Sopenharmony_ci int template_len = strlen(str); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (template_setup_done) 11062306a36Sopenharmony_ci return 1; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (!ima_template) 11362306a36Sopenharmony_ci ima_init_template_list(); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* 11662306a36Sopenharmony_ci * Verify that a template with the supplied name exists. 11762306a36Sopenharmony_ci * If not, use CONFIG_IMA_DEFAULT_TEMPLATE. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci template_desc = lookup_template_desc(str); 12062306a36Sopenharmony_ci if (!template_desc) { 12162306a36Sopenharmony_ci pr_err("template %s not found, using %s\n", 12262306a36Sopenharmony_ci str, CONFIG_IMA_DEFAULT_TEMPLATE); 12362306a36Sopenharmony_ci return 1; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* 12762306a36Sopenharmony_ci * Verify whether the current hash algorithm is supported 12862306a36Sopenharmony_ci * by the 'ima' template. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci if (template_len == 3 && strcmp(str, IMA_TEMPLATE_IMA_NAME) == 0 && 13162306a36Sopenharmony_ci ima_hash_algo != HASH_ALGO_SHA1 && ima_hash_algo != HASH_ALGO_MD5) { 13262306a36Sopenharmony_ci pr_err("template does not support hash alg\n"); 13362306a36Sopenharmony_ci return 1; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci ima_template = template_desc; 13762306a36Sopenharmony_ci template_setup_done = 1; 13862306a36Sopenharmony_ci return 1; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci__setup("ima_template=", ima_template_setup); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int __init ima_template_fmt_setup(char *str) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int num_templates = ARRAY_SIZE(builtin_templates); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (template_setup_done) 14762306a36Sopenharmony_ci return 1; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (template_desc_init_fields(str, NULL, NULL) < 0) { 15062306a36Sopenharmony_ci pr_err("format string '%s' not valid, using template %s\n", 15162306a36Sopenharmony_ci str, CONFIG_IMA_DEFAULT_TEMPLATE); 15262306a36Sopenharmony_ci return 1; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci builtin_templates[num_templates - 1].fmt = str; 15662306a36Sopenharmony_ci ima_template = builtin_templates + num_templates - 1; 15762306a36Sopenharmony_ci template_setup_done = 1; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return 1; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci__setup("ima_template_fmt=", ima_template_fmt_setup); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistruct ima_template_desc *lookup_template_desc(const char *name) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct ima_template_desc *template_desc; 16662306a36Sopenharmony_ci int found = 0; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci rcu_read_lock(); 16962306a36Sopenharmony_ci list_for_each_entry_rcu(template_desc, &defined_templates, list) { 17062306a36Sopenharmony_ci if ((strcmp(template_desc->name, name) == 0) || 17162306a36Sopenharmony_ci (strcmp(template_desc->fmt, name) == 0)) { 17262306a36Sopenharmony_ci found = 1; 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci rcu_read_unlock(); 17762306a36Sopenharmony_ci return found ? template_desc : NULL; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic const struct ima_template_field * 18162306a36Sopenharmony_cilookup_template_field(const char *field_id) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci int i; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(supported_fields); i++) 18662306a36Sopenharmony_ci if (strncmp(supported_fields[i].field_id, field_id, 18762306a36Sopenharmony_ci IMA_TEMPLATE_FIELD_ID_MAX_LEN) == 0) 18862306a36Sopenharmony_ci return &supported_fields[i]; 18962306a36Sopenharmony_ci return NULL; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int template_fmt_size(const char *template_fmt) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci char c; 19562306a36Sopenharmony_ci int template_fmt_len = strlen(template_fmt); 19662306a36Sopenharmony_ci int i = 0, j = 0; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci while (i < template_fmt_len) { 19962306a36Sopenharmony_ci c = template_fmt[i]; 20062306a36Sopenharmony_ci if (c == '|') 20162306a36Sopenharmony_ci j++; 20262306a36Sopenharmony_ci i++; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return j + 1; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciint template_desc_init_fields(const char *template_fmt, 20962306a36Sopenharmony_ci const struct ima_template_field ***fields, 21062306a36Sopenharmony_ci int *num_fields) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci const char *template_fmt_ptr; 21362306a36Sopenharmony_ci const struct ima_template_field *found_fields[IMA_TEMPLATE_NUM_FIELDS_MAX]; 21462306a36Sopenharmony_ci int template_num_fields; 21562306a36Sopenharmony_ci int i, len; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (num_fields && *num_fields > 0) /* already initialized? */ 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci template_num_fields = template_fmt_size(template_fmt); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) { 22362306a36Sopenharmony_ci pr_err("format string '%s' contains too many fields\n", 22462306a36Sopenharmony_ci template_fmt); 22562306a36Sopenharmony_ci return -EINVAL; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci for (i = 0, template_fmt_ptr = template_fmt; i < template_num_fields; 22962306a36Sopenharmony_ci i++, template_fmt_ptr += len + 1) { 23062306a36Sopenharmony_ci char tmp_field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN + 1]; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci len = strchrnul(template_fmt_ptr, '|') - template_fmt_ptr; 23362306a36Sopenharmony_ci if (len == 0 || len > IMA_TEMPLATE_FIELD_ID_MAX_LEN) { 23462306a36Sopenharmony_ci pr_err("Invalid field with length %d\n", len); 23562306a36Sopenharmony_ci return -EINVAL; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci memcpy(tmp_field_id, template_fmt_ptr, len); 23962306a36Sopenharmony_ci tmp_field_id[len] = '\0'; 24062306a36Sopenharmony_ci found_fields[i] = lookup_template_field(tmp_field_id); 24162306a36Sopenharmony_ci if (!found_fields[i]) { 24262306a36Sopenharmony_ci pr_err("field '%s' not found\n", tmp_field_id); 24362306a36Sopenharmony_ci return -ENOENT; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (fields && num_fields) { 24862306a36Sopenharmony_ci *fields = kmalloc_array(i, sizeof(**fields), GFP_KERNEL); 24962306a36Sopenharmony_ci if (*fields == NULL) 25062306a36Sopenharmony_ci return -ENOMEM; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci memcpy(*fields, found_fields, i * sizeof(**fields)); 25362306a36Sopenharmony_ci *num_fields = i; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_civoid ima_init_template_list(void) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci int i; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (!list_empty(&defined_templates)) 26462306a36Sopenharmony_ci return; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci spin_lock(&template_list); 26762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(builtin_templates); i++) { 26862306a36Sopenharmony_ci list_add_tail_rcu(&builtin_templates[i].list, 26962306a36Sopenharmony_ci &defined_templates); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci spin_unlock(&template_list); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistruct ima_template_desc *ima_template_desc_current(void) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci if (!ima_template) { 27762306a36Sopenharmony_ci ima_init_template_list(); 27862306a36Sopenharmony_ci ima_template = 27962306a36Sopenharmony_ci lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci return ima_template; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistruct ima_template_desc *ima_template_desc_buf(void) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci if (!ima_buf_template) { 28762306a36Sopenharmony_ci ima_init_template_list(); 28862306a36Sopenharmony_ci ima_buf_template = lookup_template_desc("ima-buf"); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci return ima_buf_template; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ciint __init ima_init_template(void) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct ima_template_desc *template = ima_template_desc_current(); 29662306a36Sopenharmony_ci int result; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci result = template_desc_init_fields(template->fmt, 29962306a36Sopenharmony_ci &(template->fields), 30062306a36Sopenharmony_ci &(template->num_fields)); 30162306a36Sopenharmony_ci if (result < 0) { 30262306a36Sopenharmony_ci pr_err("template %s init failed, result: %d\n", 30362306a36Sopenharmony_ci (strlen(template->name) ? 30462306a36Sopenharmony_ci template->name : template->fmt), result); 30562306a36Sopenharmony_ci return result; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci template = ima_template_desc_buf(); 30962306a36Sopenharmony_ci if (!template) { 31062306a36Sopenharmony_ci pr_err("Failed to get ima-buf template\n"); 31162306a36Sopenharmony_ci return -EINVAL; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci result = template_desc_init_fields(template->fmt, 31562306a36Sopenharmony_ci &(template->fields), 31662306a36Sopenharmony_ci &(template->num_fields)); 31762306a36Sopenharmony_ci if (result < 0) 31862306a36Sopenharmony_ci pr_err("template %s init failed, result: %d\n", 31962306a36Sopenharmony_ci (strlen(template->name) ? 32062306a36Sopenharmony_ci template->name : template->fmt), result); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return result; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic struct ima_template_desc *restore_template_fmt(char *template_name) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct ima_template_desc *template_desc = NULL; 32862306a36Sopenharmony_ci int ret; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci ret = template_desc_init_fields(template_name, NULL, NULL); 33162306a36Sopenharmony_ci if (ret < 0) { 33262306a36Sopenharmony_ci pr_err("attempting to initialize the template \"%s\" failed\n", 33362306a36Sopenharmony_ci template_name); 33462306a36Sopenharmony_ci goto out; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci template_desc = kzalloc(sizeof(*template_desc), GFP_KERNEL); 33862306a36Sopenharmony_ci if (!template_desc) 33962306a36Sopenharmony_ci goto out; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci template_desc->name = ""; 34262306a36Sopenharmony_ci template_desc->fmt = kstrdup(template_name, GFP_KERNEL); 34362306a36Sopenharmony_ci if (!template_desc->fmt) { 34462306a36Sopenharmony_ci kfree(template_desc); 34562306a36Sopenharmony_ci template_desc = NULL; 34662306a36Sopenharmony_ci goto out; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci spin_lock(&template_list); 35062306a36Sopenharmony_ci list_add_tail_rcu(&template_desc->list, &defined_templates); 35162306a36Sopenharmony_ci spin_unlock(&template_list); 35262306a36Sopenharmony_ciout: 35362306a36Sopenharmony_ci return template_desc; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int ima_restore_template_data(struct ima_template_desc *template_desc, 35762306a36Sopenharmony_ci void *template_data, 35862306a36Sopenharmony_ci int template_data_size, 35962306a36Sopenharmony_ci struct ima_template_entry **entry) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct tpm_digest *digests; 36262306a36Sopenharmony_ci int ret = 0; 36362306a36Sopenharmony_ci int i; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci *entry = kzalloc(struct_size(*entry, template_data, 36662306a36Sopenharmony_ci template_desc->num_fields), GFP_NOFS); 36762306a36Sopenharmony_ci if (!*entry) 36862306a36Sopenharmony_ci return -ENOMEM; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci digests = kcalloc(NR_BANKS(ima_tpm_chip) + ima_extra_slots, 37162306a36Sopenharmony_ci sizeof(*digests), GFP_NOFS); 37262306a36Sopenharmony_ci if (!digests) { 37362306a36Sopenharmony_ci kfree(*entry); 37462306a36Sopenharmony_ci return -ENOMEM; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci (*entry)->digests = digests; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ret = ima_parse_buf(template_data, template_data + template_data_size, 38062306a36Sopenharmony_ci NULL, template_desc->num_fields, 38162306a36Sopenharmony_ci (*entry)->template_data, NULL, NULL, 38262306a36Sopenharmony_ci ENFORCE_FIELDS | ENFORCE_BUFEND, "template data"); 38362306a36Sopenharmony_ci if (ret < 0) { 38462306a36Sopenharmony_ci kfree((*entry)->digests); 38562306a36Sopenharmony_ci kfree(*entry); 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci (*entry)->template_desc = template_desc; 39062306a36Sopenharmony_ci for (i = 0; i < template_desc->num_fields; i++) { 39162306a36Sopenharmony_ci struct ima_field_data *field_data = &(*entry)->template_data[i]; 39262306a36Sopenharmony_ci u8 *data = field_data->data; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci (*entry)->template_data[i].data = 39562306a36Sopenharmony_ci kzalloc(field_data->len + 1, GFP_KERNEL); 39662306a36Sopenharmony_ci if (!(*entry)->template_data[i].data) { 39762306a36Sopenharmony_ci ret = -ENOMEM; 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci memcpy((*entry)->template_data[i].data, data, field_data->len); 40162306a36Sopenharmony_ci (*entry)->template_data_len += sizeof(field_data->len); 40262306a36Sopenharmony_ci (*entry)->template_data_len += field_data->len; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (ret < 0) { 40662306a36Sopenharmony_ci ima_free_template_entry(*entry); 40762306a36Sopenharmony_ci *entry = NULL; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci return ret; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* Restore the serialized binary measurement list without extending PCRs. */ 41462306a36Sopenharmony_ciint ima_restore_measurement_list(loff_t size, void *buf) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci char template_name[MAX_TEMPLATE_NAME_LEN]; 41762306a36Sopenharmony_ci unsigned char zero[TPM_DIGEST_SIZE] = { 0 }; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci struct ima_kexec_hdr *khdr = buf; 42062306a36Sopenharmony_ci struct ima_field_data hdr[HDR__LAST] = { 42162306a36Sopenharmony_ci [HDR_PCR] = {.len = sizeof(u32)}, 42262306a36Sopenharmony_ci [HDR_DIGEST] = {.len = TPM_DIGEST_SIZE}, 42362306a36Sopenharmony_ci }; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci void *bufp = buf + sizeof(*khdr); 42662306a36Sopenharmony_ci void *bufendp; 42762306a36Sopenharmony_ci struct ima_template_entry *entry; 42862306a36Sopenharmony_ci struct ima_template_desc *template_desc; 42962306a36Sopenharmony_ci DECLARE_BITMAP(hdr_mask, HDR__LAST); 43062306a36Sopenharmony_ci unsigned long count = 0; 43162306a36Sopenharmony_ci int ret = 0; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (!buf || size < sizeof(*khdr)) 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (ima_canonical_fmt) { 43762306a36Sopenharmony_ci khdr->version = le16_to_cpu((__force __le16)khdr->version); 43862306a36Sopenharmony_ci khdr->count = le64_to_cpu((__force __le64)khdr->count); 43962306a36Sopenharmony_ci khdr->buffer_size = le64_to_cpu((__force __le64)khdr->buffer_size); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (khdr->version != 1) { 44362306a36Sopenharmony_ci pr_err("attempting to restore a incompatible measurement list"); 44462306a36Sopenharmony_ci return -EINVAL; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (khdr->count > ULONG_MAX - 1) { 44862306a36Sopenharmony_ci pr_err("attempting to restore too many measurements"); 44962306a36Sopenharmony_ci return -EINVAL; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci bitmap_zero(hdr_mask, HDR__LAST); 45362306a36Sopenharmony_ci bitmap_set(hdr_mask, HDR_PCR, 1); 45462306a36Sopenharmony_ci bitmap_set(hdr_mask, HDR_DIGEST, 1); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* 45762306a36Sopenharmony_ci * ima kexec buffer prefix: version, buffer size, count 45862306a36Sopenharmony_ci * v1 format: pcr, digest, template-name-len, template-name, 45962306a36Sopenharmony_ci * template-data-size, template-data 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci bufendp = buf + khdr->buffer_size; 46262306a36Sopenharmony_ci while ((bufp < bufendp) && (count++ < khdr->count)) { 46362306a36Sopenharmony_ci int enforce_mask = ENFORCE_FIELDS; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci enforce_mask |= (count == khdr->count) ? ENFORCE_BUFEND : 0; 46662306a36Sopenharmony_ci ret = ima_parse_buf(bufp, bufendp, &bufp, HDR__LAST, hdr, NULL, 46762306a36Sopenharmony_ci hdr_mask, enforce_mask, "entry header"); 46862306a36Sopenharmony_ci if (ret < 0) 46962306a36Sopenharmony_ci break; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (hdr[HDR_TEMPLATE_NAME].len >= MAX_TEMPLATE_NAME_LEN) { 47262306a36Sopenharmony_ci pr_err("attempting to restore a template name that is too long\n"); 47362306a36Sopenharmony_ci ret = -EINVAL; 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* template name is not null terminated */ 47862306a36Sopenharmony_ci memcpy(template_name, hdr[HDR_TEMPLATE_NAME].data, 47962306a36Sopenharmony_ci hdr[HDR_TEMPLATE_NAME].len); 48062306a36Sopenharmony_ci template_name[hdr[HDR_TEMPLATE_NAME].len] = 0; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (strcmp(template_name, "ima") == 0) { 48362306a36Sopenharmony_ci pr_err("attempting to restore an unsupported template \"%s\" failed\n", 48462306a36Sopenharmony_ci template_name); 48562306a36Sopenharmony_ci ret = -EINVAL; 48662306a36Sopenharmony_ci break; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci template_desc = lookup_template_desc(template_name); 49062306a36Sopenharmony_ci if (!template_desc) { 49162306a36Sopenharmony_ci template_desc = restore_template_fmt(template_name); 49262306a36Sopenharmony_ci if (!template_desc) 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* 49762306a36Sopenharmony_ci * Only the running system's template format is initialized 49862306a36Sopenharmony_ci * on boot. As needed, initialize the other template formats. 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci ret = template_desc_init_fields(template_desc->fmt, 50162306a36Sopenharmony_ci &(template_desc->fields), 50262306a36Sopenharmony_ci &(template_desc->num_fields)); 50362306a36Sopenharmony_ci if (ret < 0) { 50462306a36Sopenharmony_ci pr_err("attempting to restore the template fmt \"%s\" failed\n", 50562306a36Sopenharmony_ci template_desc->fmt); 50662306a36Sopenharmony_ci ret = -EINVAL; 50762306a36Sopenharmony_ci break; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci ret = ima_restore_template_data(template_desc, 51162306a36Sopenharmony_ci hdr[HDR_TEMPLATE_DATA].data, 51262306a36Sopenharmony_ci hdr[HDR_TEMPLATE_DATA].len, 51362306a36Sopenharmony_ci &entry); 51462306a36Sopenharmony_ci if (ret < 0) 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (memcmp(hdr[HDR_DIGEST].data, zero, sizeof(zero))) { 51862306a36Sopenharmony_ci ret = ima_calc_field_array_hash( 51962306a36Sopenharmony_ci &entry->template_data[0], 52062306a36Sopenharmony_ci entry); 52162306a36Sopenharmony_ci if (ret < 0) { 52262306a36Sopenharmony_ci pr_err("cannot calculate template digest\n"); 52362306a36Sopenharmony_ci ret = -EINVAL; 52462306a36Sopenharmony_ci break; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci entry->pcr = !ima_canonical_fmt ? *(u32 *)(hdr[HDR_PCR].data) : 52962306a36Sopenharmony_ci le32_to_cpu(*(__le32 *)(hdr[HDR_PCR].data)); 53062306a36Sopenharmony_ci ret = ima_restore_measurement_entry(entry); 53162306a36Sopenharmony_ci if (ret < 0) 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci return ret; 53662306a36Sopenharmony_ci} 537