123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- /*
- * Copyright (C) 2013 Politecnico di Torino, Italy
- * TORSEC group -- http://security.polito.it
- *
- * Author: Roberto Sassu <roberto.sassu@polito.it>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2 of the
- * License.
- *
- * File: ima_template.c
- * Helpers to manage template descriptors.
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/rculist.h>
- #include "ima.h"
- #include "ima_template_lib.h"
- enum header_fields { HDR_PCR, HDR_DIGEST, HDR_TEMPLATE_NAME,
- HDR_TEMPLATE_DATA, HDR__LAST };
- static struct ima_template_desc builtin_templates[] = {
- {.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
- {.name = "ima-ng", .fmt = "d-ng|n-ng"},
- {.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
- {.name = "", .fmt = ""}, /* placeholder for a custom format */
- };
- static LIST_HEAD(defined_templates);
- static DEFINE_SPINLOCK(template_list);
- static struct ima_template_field supported_fields[] = {
- {.field_id = "d", .field_init = ima_eventdigest_init,
- .field_show = ima_show_template_digest},
- {.field_id = "n", .field_init = ima_eventname_init,
- .field_show = ima_show_template_string},
- {.field_id = "d-ng", .field_init = ima_eventdigest_ng_init,
- .field_show = ima_show_template_digest_ng},
- {.field_id = "n-ng", .field_init = ima_eventname_ng_init,
- .field_show = ima_show_template_string},
- {.field_id = "sig", .field_init = ima_eventsig_init,
- .field_show = ima_show_template_sig},
- };
- #define MAX_TEMPLATE_NAME_LEN 15
- static struct ima_template_desc *ima_template;
- static struct ima_template_desc *lookup_template_desc(const char *name);
- static int template_desc_init_fields(const char *template_fmt,
- struct ima_template_field ***fields,
- int *num_fields);
- static int __init ima_template_setup(char *str)
- {
- struct ima_template_desc *template_desc;
- int template_len = strlen(str);
- if (ima_template)
- return 1;
- ima_init_template_list();
- /*
- * Verify that a template with the supplied name exists.
- * If not, use CONFIG_IMA_DEFAULT_TEMPLATE.
- */
- template_desc = lookup_template_desc(str);
- if (!template_desc) {
- pr_err("template %s not found, using %s\n",
- str, CONFIG_IMA_DEFAULT_TEMPLATE);
- return 1;
- }
- /*
- * Verify whether the current hash algorithm is supported
- * by the 'ima' template.
- */
- if (template_len == 3 && strcmp(str, IMA_TEMPLATE_IMA_NAME) == 0 &&
- ima_hash_algo != HASH_ALGO_SHA1 && ima_hash_algo != HASH_ALGO_MD5) {
- pr_err("template does not support hash alg\n");
- return 1;
- }
- ima_template = template_desc;
- return 1;
- }
- __setup("ima_template=", ima_template_setup);
- static int __init ima_template_fmt_setup(char *str)
- {
- int num_templates = ARRAY_SIZE(builtin_templates);
- if (ima_template)
- return 1;
- if (template_desc_init_fields(str, NULL, NULL) < 0) {
- pr_err("format string '%s' not valid, using template %s\n",
- str, CONFIG_IMA_DEFAULT_TEMPLATE);
- return 1;
- }
- builtin_templates[num_templates - 1].fmt = str;
- ima_template = builtin_templates + num_templates - 1;
- return 1;
- }
- __setup("ima_template_fmt=", ima_template_fmt_setup);
- static struct ima_template_desc *lookup_template_desc(const char *name)
- {
- struct ima_template_desc *template_desc;
- int found = 0;
- rcu_read_lock();
- list_for_each_entry_rcu(template_desc, &defined_templates, list) {
- if ((strcmp(template_desc->name, name) == 0) ||
- (strcmp(template_desc->fmt, name) == 0)) {
- found = 1;
- break;
- }
- }
- rcu_read_unlock();
- return found ? template_desc : NULL;
- }
- static struct ima_template_field *lookup_template_field(const char *field_id)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(supported_fields); i++)
- if (strncmp(supported_fields[i].field_id, field_id,
- IMA_TEMPLATE_FIELD_ID_MAX_LEN) == 0)
- return &supported_fields[i];
- return NULL;
- }
- static int template_fmt_size(const char *template_fmt)
- {
- char c;
- int template_fmt_len = strlen(template_fmt);
- int i = 0, j = 0;
- while (i < template_fmt_len) {
- c = template_fmt[i];
- if (c == '|')
- j++;
- i++;
- }
- return j + 1;
- }
- static int template_desc_init_fields(const char *template_fmt,
- struct ima_template_field ***fields,
- int *num_fields)
- {
- const char *template_fmt_ptr;
- struct ima_template_field *found_fields[IMA_TEMPLATE_NUM_FIELDS_MAX];
- int template_num_fields;
- int i, len;
- if (num_fields && *num_fields > 0) /* already initialized? */
- return 0;
- template_num_fields = template_fmt_size(template_fmt);
- if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX) {
- pr_err("format string '%s' contains too many fields\n",
- template_fmt);
- return -EINVAL;
- }
- for (i = 0, template_fmt_ptr = template_fmt; i < template_num_fields;
- i++, template_fmt_ptr += len + 1) {
- char tmp_field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN + 1];
- len = strchrnul(template_fmt_ptr, '|') - template_fmt_ptr;
- if (len == 0 || len > IMA_TEMPLATE_FIELD_ID_MAX_LEN) {
- pr_err("Invalid field with length %d\n", len);
- return -EINVAL;
- }
- memcpy(tmp_field_id, template_fmt_ptr, len);
- tmp_field_id[len] = '\0';
- found_fields[i] = lookup_template_field(tmp_field_id);
- if (!found_fields[i]) {
- pr_err("field '%s' not found\n", tmp_field_id);
- return -ENOENT;
- }
- }
- if (fields && num_fields) {
- *fields = kmalloc_array(i, sizeof(*fields), GFP_KERNEL);
- if (*fields == NULL)
- return -ENOMEM;
- memcpy(*fields, found_fields, i * sizeof(*fields));
- *num_fields = i;
- }
- return 0;
- }
- void ima_init_template_list(void)
- {
- int i;
- if (!list_empty(&defined_templates))
- return;
- spin_lock(&template_list);
- for (i = 0; i < ARRAY_SIZE(builtin_templates); i++) {
- list_add_tail_rcu(&builtin_templates[i].list,
- &defined_templates);
- }
- spin_unlock(&template_list);
- }
- struct ima_template_desc *ima_template_desc_current(void)
- {
- if (!ima_template) {
- ima_init_template_list();
- ima_template =
- lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE);
- }
- return ima_template;
- }
- int __init ima_init_template(void)
- {
- struct ima_template_desc *template = ima_template_desc_current();
- int result;
- result = template_desc_init_fields(template->fmt,
- &(template->fields),
- &(template->num_fields));
- if (result < 0)
- pr_err("template %s init failed, result: %d\n",
- (strlen(template->name) ?
- template->name : template->fmt), result);
- return result;
- }
- static struct ima_template_desc *restore_template_fmt(char *template_name)
- {
- struct ima_template_desc *template_desc = NULL;
- int ret;
- ret = template_desc_init_fields(template_name, NULL, NULL);
- if (ret < 0) {
- pr_err("attempting to initialize the template \"%s\" failed\n",
- template_name);
- goto out;
- }
- template_desc = kzalloc(sizeof(*template_desc), GFP_KERNEL);
- if (!template_desc)
- goto out;
- template_desc->name = "";
- template_desc->fmt = kstrdup(template_name, GFP_KERNEL);
- if (!template_desc->fmt)
- goto out;
- spin_lock(&template_list);
- list_add_tail_rcu(&template_desc->list, &defined_templates);
- spin_unlock(&template_list);
- out:
- return template_desc;
- }
- static int ima_restore_template_data(struct ima_template_desc *template_desc,
- void *template_data,
- int template_data_size,
- struct ima_template_entry **entry)
- {
- int ret = 0;
- int i;
- *entry = kzalloc(sizeof(**entry) +
- template_desc->num_fields * sizeof(struct ima_field_data),
- GFP_NOFS);
- if (!*entry)
- return -ENOMEM;
- ret = ima_parse_buf(template_data, template_data + template_data_size,
- NULL, template_desc->num_fields,
- (*entry)->template_data, NULL, NULL,
- ENFORCE_FIELDS | ENFORCE_BUFEND, "template data");
- if (ret < 0) {
- kfree(*entry);
- return ret;
- }
- (*entry)->template_desc = template_desc;
- for (i = 0; i < template_desc->num_fields; i++) {
- struct ima_field_data *field_data = &(*entry)->template_data[i];
- u8 *data = field_data->data;
- (*entry)->template_data[i].data =
- kzalloc(field_data->len + 1, GFP_KERNEL);
- if (!(*entry)->template_data[i].data) {
- ret = -ENOMEM;
- break;
- }
- memcpy((*entry)->template_data[i].data, data, field_data->len);
- (*entry)->template_data_len += sizeof(field_data->len);
- (*entry)->template_data_len += field_data->len;
- }
- if (ret < 0) {
- ima_free_template_entry(*entry);
- *entry = NULL;
- }
- return ret;
- }
- /* Restore the serialized binary measurement list without extending PCRs. */
- int ima_restore_measurement_list(loff_t size, void *buf)
- {
- char template_name[MAX_TEMPLATE_NAME_LEN];
- struct ima_kexec_hdr *khdr = buf;
- struct ima_field_data hdr[HDR__LAST] = {
- [HDR_PCR] = {.len = sizeof(u32)},
- [HDR_DIGEST] = {.len = TPM_DIGEST_SIZE},
- };
- void *bufp = buf + sizeof(*khdr);
- void *bufendp;
- struct ima_template_entry *entry;
- struct ima_template_desc *template_desc;
- DECLARE_BITMAP(hdr_mask, HDR__LAST);
- unsigned long count = 0;
- int ret = 0;
- if (!buf || size < sizeof(*khdr))
- return 0;
- if (ima_canonical_fmt) {
- khdr->version = le16_to_cpu(khdr->version);
- khdr->count = le64_to_cpu(khdr->count);
- khdr->buffer_size = le64_to_cpu(khdr->buffer_size);
- }
- if (khdr->version != 1) {
- pr_err("attempting to restore a incompatible measurement list");
- return -EINVAL;
- }
- if (khdr->count > ULONG_MAX - 1) {
- pr_err("attempting to restore too many measurements");
- return -EINVAL;
- }
- bitmap_zero(hdr_mask, HDR__LAST);
- bitmap_set(hdr_mask, HDR_PCR, 1);
- bitmap_set(hdr_mask, HDR_DIGEST, 1);
- /*
- * ima kexec buffer prefix: version, buffer size, count
- * v1 format: pcr, digest, template-name-len, template-name,
- * template-data-size, template-data
- */
- bufendp = buf + khdr->buffer_size;
- while ((bufp < bufendp) && (count++ < khdr->count)) {
- int enforce_mask = ENFORCE_FIELDS;
- enforce_mask |= (count == khdr->count) ? ENFORCE_BUFEND : 0;
- ret = ima_parse_buf(bufp, bufendp, &bufp, HDR__LAST, hdr, NULL,
- hdr_mask, enforce_mask, "entry header");
- if (ret < 0)
- break;
- if (hdr[HDR_TEMPLATE_NAME].len >= MAX_TEMPLATE_NAME_LEN) {
- pr_err("attempting to restore a template name that is too long\n");
- ret = -EINVAL;
- break;
- }
- /* template name is not null terminated */
- memcpy(template_name, hdr[HDR_TEMPLATE_NAME].data,
- hdr[HDR_TEMPLATE_NAME].len);
- template_name[hdr[HDR_TEMPLATE_NAME].len] = 0;
- if (strcmp(template_name, "ima") == 0) {
- pr_err("attempting to restore an unsupported template \"%s\" failed\n",
- template_name);
- ret = -EINVAL;
- break;
- }
- template_desc = lookup_template_desc(template_name);
- if (!template_desc) {
- template_desc = restore_template_fmt(template_name);
- if (!template_desc)
- break;
- }
- /*
- * Only the running system's template format is initialized
- * on boot. As needed, initialize the other template formats.
- */
- ret = template_desc_init_fields(template_desc->fmt,
- &(template_desc->fields),
- &(template_desc->num_fields));
- if (ret < 0) {
- pr_err("attempting to restore the template fmt \"%s\" failed\n",
- template_desc->fmt);
- ret = -EINVAL;
- break;
- }
- ret = ima_restore_template_data(template_desc,
- hdr[HDR_TEMPLATE_DATA].data,
- hdr[HDR_TEMPLATE_DATA].len,
- &entry);
- if (ret < 0)
- break;
- memcpy(entry->digest, hdr[HDR_DIGEST].data,
- hdr[HDR_DIGEST].len);
- entry->pcr = !ima_canonical_fmt ? *(hdr[HDR_PCR].data) :
- le32_to_cpu(*(hdr[HDR_PCR].data));
- ret = ima_restore_measurement_entry(entry);
- if (ret < 0)
- break;
- }
- return ret;
- }
|