| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2013 Politecnico di Torino, Italy |
|---|
| 3 | | - * TORSEC group -- http://security.polito.it |
|---|
| 4 | + * TORSEC group -- https://security.polito.it |
|---|
| 4 | 5 | * |
|---|
| 5 | 6 | * Author: Roberto Sassu <roberto.sassu@polito.it> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or |
|---|
| 8 | | - * modify it under the terms of the GNU General Public License as |
|---|
| 9 | | - * published by the Free Software Foundation, version 2 of the |
|---|
| 10 | | - * License. |
|---|
| 11 | 7 | * |
|---|
| 12 | 8 | * File: ima_template.c |
|---|
| 13 | 9 | * Helpers to manage template descriptors. |
|---|
| 14 | 10 | */ |
|---|
| 15 | | - |
|---|
| 16 | | -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|---|
| 17 | 11 | |
|---|
| 18 | 12 | #include <linux/rculist.h> |
|---|
| 19 | 13 | #include "ima.h" |
|---|
| .. | .. |
|---|
| 26 | 20 | {.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT}, |
|---|
| 27 | 21 | {.name = "ima-ng", .fmt = "d-ng|n-ng"}, |
|---|
| 28 | 22 | {.name = "ima-sig", .fmt = "d-ng|n-ng|sig"}, |
|---|
| 23 | + {.name = "ima-buf", .fmt = "d-ng|n-ng|buf"}, |
|---|
| 24 | + {.name = "ima-modsig", .fmt = "d-ng|n-ng|sig|d-modsig|modsig"}, |
|---|
| 29 | 25 | {.name = "", .fmt = ""}, /* placeholder for a custom format */ |
|---|
| 30 | 26 | }; |
|---|
| 31 | 27 | |
|---|
| .. | .. |
|---|
| 33 | 29 | static DEFINE_SPINLOCK(template_list); |
|---|
| 34 | 30 | static int template_setup_done; |
|---|
| 35 | 31 | |
|---|
| 36 | | -static struct ima_template_field supported_fields[] = { |
|---|
| 32 | +static const struct ima_template_field supported_fields[] = { |
|---|
| 37 | 33 | {.field_id = "d", .field_init = ima_eventdigest_init, |
|---|
| 38 | 34 | .field_show = ima_show_template_digest}, |
|---|
| 39 | 35 | {.field_id = "n", .field_init = ima_eventname_init, |
|---|
| .. | .. |
|---|
| 44 | 40 | .field_show = ima_show_template_string}, |
|---|
| 45 | 41 | {.field_id = "sig", .field_init = ima_eventsig_init, |
|---|
| 46 | 42 | .field_show = ima_show_template_sig}, |
|---|
| 43 | + {.field_id = "buf", .field_init = ima_eventbuf_init, |
|---|
| 44 | + .field_show = ima_show_template_buf}, |
|---|
| 45 | + {.field_id = "d-modsig", .field_init = ima_eventdigest_modsig_init, |
|---|
| 46 | + .field_show = ima_show_template_digest_ng}, |
|---|
| 47 | + {.field_id = "modsig", .field_init = ima_eventmodsig_init, |
|---|
| 48 | + .field_show = ima_show_template_sig}, |
|---|
| 47 | 49 | }; |
|---|
| 48 | | -#define MAX_TEMPLATE_NAME_LEN 15 |
|---|
| 50 | + |
|---|
| 51 | +/* |
|---|
| 52 | + * Used when restoring measurements carried over from a kexec. 'd' and 'n' don't |
|---|
| 53 | + * need to be accounted for since they shouldn't be defined in the same template |
|---|
| 54 | + * description as 'd-ng' and 'n-ng' respectively. |
|---|
| 55 | + */ |
|---|
| 56 | +#define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf|d-modisg|modsig") |
|---|
| 49 | 57 | |
|---|
| 50 | 58 | static struct ima_template_desc *ima_template; |
|---|
| 51 | | -static struct ima_template_desc *lookup_template_desc(const char *name); |
|---|
| 52 | | -static int template_desc_init_fields(const char *template_fmt, |
|---|
| 53 | | - struct ima_template_field ***fields, |
|---|
| 54 | | - int *num_fields); |
|---|
| 59 | + |
|---|
| 60 | +/** |
|---|
| 61 | + * ima_template_has_modsig - Check whether template has modsig-related fields. |
|---|
| 62 | + * @ima_template: IMA template to check. |
|---|
| 63 | + * |
|---|
| 64 | + * Tells whether the given template has fields referencing a file's appended |
|---|
| 65 | + * signature. |
|---|
| 66 | + */ |
|---|
| 67 | +bool ima_template_has_modsig(const struct ima_template_desc *ima_template) |
|---|
| 68 | +{ |
|---|
| 69 | + int i; |
|---|
| 70 | + |
|---|
| 71 | + for (i = 0; i < ima_template->num_fields; i++) |
|---|
| 72 | + if (!strcmp(ima_template->fields[i]->field_id, "modsig") || |
|---|
| 73 | + !strcmp(ima_template->fields[i]->field_id, "d-modsig")) |
|---|
| 74 | + return true; |
|---|
| 75 | + |
|---|
| 76 | + return false; |
|---|
| 77 | +} |
|---|
| 55 | 78 | |
|---|
| 56 | 79 | static int __init ima_template_setup(char *str) |
|---|
| 57 | 80 | { |
|---|
| .. | .. |
|---|
| 112 | 135 | } |
|---|
| 113 | 136 | __setup("ima_template_fmt=", ima_template_fmt_setup); |
|---|
| 114 | 137 | |
|---|
| 115 | | -static struct ima_template_desc *lookup_template_desc(const char *name) |
|---|
| 138 | +struct ima_template_desc *lookup_template_desc(const char *name) |
|---|
| 116 | 139 | { |
|---|
| 117 | 140 | struct ima_template_desc *template_desc; |
|---|
| 118 | 141 | int found = 0; |
|---|
| .. | .. |
|---|
| 129 | 152 | return found ? template_desc : NULL; |
|---|
| 130 | 153 | } |
|---|
| 131 | 154 | |
|---|
| 132 | | -static struct ima_template_field *lookup_template_field(const char *field_id) |
|---|
| 155 | +static const struct ima_template_field * |
|---|
| 156 | +lookup_template_field(const char *field_id) |
|---|
| 133 | 157 | { |
|---|
| 134 | 158 | int i; |
|---|
| 135 | 159 | |
|---|
| .. | .. |
|---|
| 156 | 180 | return j + 1; |
|---|
| 157 | 181 | } |
|---|
| 158 | 182 | |
|---|
| 159 | | -static int template_desc_init_fields(const char *template_fmt, |
|---|
| 160 | | - struct ima_template_field ***fields, |
|---|
| 161 | | - int *num_fields) |
|---|
| 183 | +int template_desc_init_fields(const char *template_fmt, |
|---|
| 184 | + const struct ima_template_field ***fields, |
|---|
| 185 | + int *num_fields) |
|---|
| 162 | 186 | { |
|---|
| 163 | 187 | const char *template_fmt_ptr; |
|---|
| 164 | | - struct ima_template_field *found_fields[IMA_TEMPLATE_NUM_FIELDS_MAX]; |
|---|
| 188 | + const struct ima_template_field *found_fields[IMA_TEMPLATE_NUM_FIELDS_MAX]; |
|---|
| 165 | 189 | int template_num_fields; |
|---|
| 166 | 190 | int i, len; |
|---|
| 167 | 191 | |
|---|
| .. | .. |
|---|
| 196 | 220 | } |
|---|
| 197 | 221 | |
|---|
| 198 | 222 | if (fields && num_fields) { |
|---|
| 199 | | - *fields = kmalloc_array(i, sizeof(*fields), GFP_KERNEL); |
|---|
| 223 | + *fields = kmalloc_array(i, sizeof(**fields), GFP_KERNEL); |
|---|
| 200 | 224 | if (*fields == NULL) |
|---|
| 201 | 225 | return -ENOMEM; |
|---|
| 202 | 226 | |
|---|
| 203 | | - memcpy(*fields, found_fields, i * sizeof(*fields)); |
|---|
| 227 | + memcpy(*fields, found_fields, i * sizeof(**fields)); |
|---|
| 204 | 228 | *num_fields = i; |
|---|
| 205 | 229 | } |
|---|
| 206 | 230 | |
|---|
| .. | .. |
|---|
| 266 | 290 | |
|---|
| 267 | 291 | template_desc->name = ""; |
|---|
| 268 | 292 | template_desc->fmt = kstrdup(template_name, GFP_KERNEL); |
|---|
| 269 | | - if (!template_desc->fmt) |
|---|
| 293 | + if (!template_desc->fmt) { |
|---|
| 294 | + kfree(template_desc); |
|---|
| 295 | + template_desc = NULL; |
|---|
| 270 | 296 | goto out; |
|---|
| 297 | + } |
|---|
| 271 | 298 | |
|---|
| 272 | 299 | spin_lock(&template_list); |
|---|
| 273 | 300 | list_add_tail_rcu(&template_desc->list, &defined_templates); |
|---|
| .. | .. |
|---|
| 281 | 308 | int template_data_size, |
|---|
| 282 | 309 | struct ima_template_entry **entry) |
|---|
| 283 | 310 | { |
|---|
| 311 | + struct tpm_digest *digests; |
|---|
| 284 | 312 | int ret = 0; |
|---|
| 285 | 313 | int i; |
|---|
| 286 | 314 | |
|---|
| 287 | | - *entry = kzalloc(sizeof(**entry) + |
|---|
| 288 | | - template_desc->num_fields * sizeof(struct ima_field_data), |
|---|
| 289 | | - GFP_NOFS); |
|---|
| 315 | + *entry = kzalloc(struct_size(*entry, template_data, |
|---|
| 316 | + template_desc->num_fields), GFP_NOFS); |
|---|
| 290 | 317 | if (!*entry) |
|---|
| 291 | 318 | return -ENOMEM; |
|---|
| 319 | + |
|---|
| 320 | + digests = kcalloc(NR_BANKS(ima_tpm_chip) + ima_extra_slots, |
|---|
| 321 | + sizeof(*digests), GFP_NOFS); |
|---|
| 322 | + if (!digests) { |
|---|
| 323 | + kfree(*entry); |
|---|
| 324 | + return -ENOMEM; |
|---|
| 325 | + } |
|---|
| 326 | + |
|---|
| 327 | + (*entry)->digests = digests; |
|---|
| 292 | 328 | |
|---|
| 293 | 329 | ret = ima_parse_buf(template_data, template_data + template_data_size, |
|---|
| 294 | 330 | NULL, template_desc->num_fields, |
|---|
| 295 | 331 | (*entry)->template_data, NULL, NULL, |
|---|
| 296 | 332 | ENFORCE_FIELDS | ENFORCE_BUFEND, "template data"); |
|---|
| 297 | 333 | if (ret < 0) { |
|---|
| 334 | + kfree((*entry)->digests); |
|---|
| 298 | 335 | kfree(*entry); |
|---|
| 299 | 336 | return ret; |
|---|
| 300 | 337 | } |
|---|
| .. | .. |
|---|
| 327 | 364 | int ima_restore_measurement_list(loff_t size, void *buf) |
|---|
| 328 | 365 | { |
|---|
| 329 | 366 | char template_name[MAX_TEMPLATE_NAME_LEN]; |
|---|
| 367 | + unsigned char zero[TPM_DIGEST_SIZE] = { 0 }; |
|---|
| 330 | 368 | |
|---|
| 331 | 369 | struct ima_kexec_hdr *khdr = buf; |
|---|
| 332 | 370 | struct ima_field_data hdr[HDR__LAST] = { |
|---|
| .. | .. |
|---|
| 426 | 464 | if (ret < 0) |
|---|
| 427 | 465 | break; |
|---|
| 428 | 466 | |
|---|
| 429 | | - memcpy(entry->digest, hdr[HDR_DIGEST].data, |
|---|
| 430 | | - hdr[HDR_DIGEST].len); |
|---|
| 431 | | - entry->pcr = !ima_canonical_fmt ? *(hdr[HDR_PCR].data) : |
|---|
| 432 | | - le32_to_cpu(*(hdr[HDR_PCR].data)); |
|---|
| 467 | + if (memcmp(hdr[HDR_DIGEST].data, zero, sizeof(zero))) { |
|---|
| 468 | + ret = ima_calc_field_array_hash( |
|---|
| 469 | + &entry->template_data[0], |
|---|
| 470 | + entry); |
|---|
| 471 | + if (ret < 0) { |
|---|
| 472 | + pr_err("cannot calculate template digest\n"); |
|---|
| 473 | + ret = -EINVAL; |
|---|
| 474 | + break; |
|---|
| 475 | + } |
|---|
| 476 | + } |
|---|
| 477 | + |
|---|
| 478 | + entry->pcr = !ima_canonical_fmt ? *(u32 *)(hdr[HDR_PCR].data) : |
|---|
| 479 | + le32_to_cpu(*(u32 *)(hdr[HDR_PCR].data)); |
|---|
| 433 | 480 | ret = ima_restore_measurement_entry(entry); |
|---|
| 434 | 481 | if (ret < 0) |
|---|
| 435 | 482 | break; |
|---|