linux/security/integrity/ima/ima_kexec.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2016 IBM Corporation
   4 *
   5 * Authors:
   6 * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
   7 * Mimi Zohar <zohar@linux.vnet.ibm.com>
   8 */
   9
  10#include <linux/seq_file.h>
  11#include <linux/vmalloc.h>
  12#include <linux/kexec.h>
  13#include <linux/of.h>
  14#include <linux/ima.h>
  15#include "ima.h"
  16
  17#ifdef CONFIG_IMA_KEXEC
  18static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
  19                                     unsigned long segment_size)
  20{
  21        struct ima_queue_entry *qe;
  22        struct seq_file file;
  23        struct ima_kexec_hdr khdr;
  24        int ret = 0;
  25
  26        /* segment size can't change between kexec load and execute */
  27        file.buf = vmalloc(segment_size);
  28        if (!file.buf) {
  29                ret = -ENOMEM;
  30                goto out;
  31        }
  32
  33        file.size = segment_size;
  34        file.read_pos = 0;
  35        file.count = sizeof(khdr);      /* reserved space */
  36
  37        memset(&khdr, 0, sizeof(khdr));
  38        khdr.version = 1;
  39        list_for_each_entry_rcu(qe, &ima_measurements, later) {
  40                if (file.count < file.size) {
  41                        khdr.count++;
  42                        ima_measurements_show(&file, qe);
  43                } else {
  44                        ret = -EINVAL;
  45                        break;
  46                }
  47        }
  48
  49        if (ret < 0)
  50                goto out;
  51
  52        /*
  53         * fill in reserved space with some buffer details
  54         * (eg. version, buffer size, number of measurements)
  55         */
  56        khdr.buffer_size = file.count;
  57        if (ima_canonical_fmt) {
  58                khdr.version = cpu_to_le16(khdr.version);
  59                khdr.count = cpu_to_le64(khdr.count);
  60                khdr.buffer_size = cpu_to_le64(khdr.buffer_size);
  61        }
  62        memcpy(file.buf, &khdr, sizeof(khdr));
  63
  64        print_hex_dump(KERN_DEBUG, "ima dump: ", DUMP_PREFIX_NONE,
  65                        16, 1, file.buf,
  66                        file.count < 100 ? file.count : 100, true);
  67
  68        *buffer_size = file.count;
  69        *buffer = file.buf;
  70out:
  71        if (ret == -EINVAL)
  72                vfree(file.buf);
  73        return ret;
  74}
  75
  76/*
  77 * Called during kexec_file_load so that IMA can add a segment to the kexec
  78 * image for the measurement list for the next kernel.
  79 *
  80 * This function assumes that kexec_mutex is held.
  81 */
  82void ima_add_kexec_buffer(struct kimage *image)
  83{
  84        struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE,
  85                                  .buf_min = 0, .buf_max = ULONG_MAX,
  86                                  .top_down = true };
  87        unsigned long binary_runtime_size;
  88
  89        /* use more understandable variable names than defined in kbuf */
  90        void *kexec_buffer = NULL;
  91        size_t kexec_buffer_size;
  92        size_t kexec_segment_size;
  93        int ret;
  94
  95        /*
  96         * Reserve an extra half page of memory for additional measurements
  97         * added during the kexec load.
  98         */
  99        binary_runtime_size = ima_get_binary_runtime_size();
 100        if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE)
 101                kexec_segment_size = ULONG_MAX;
 102        else
 103                kexec_segment_size = ALIGN(ima_get_binary_runtime_size() +
 104                                           PAGE_SIZE / 2, PAGE_SIZE);
 105        if ((kexec_segment_size == ULONG_MAX) ||
 106            ((kexec_segment_size >> PAGE_SHIFT) > totalram_pages() / 2)) {
 107                pr_err("Binary measurement list too large.\n");
 108                return;
 109        }
 110
 111        ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer,
 112                                  kexec_segment_size);
 113        if (!kexec_buffer) {
 114                pr_err("Not enough memory for the kexec measurement buffer.\n");
 115                return;
 116        }
 117
 118        kbuf.buffer = kexec_buffer;
 119        kbuf.bufsz = kexec_buffer_size;
 120        kbuf.memsz = kexec_segment_size;
 121        ret = kexec_add_buffer(&kbuf);
 122        if (ret) {
 123                pr_err("Error passing over kexec measurement buffer.\n");
 124                vfree(kexec_buffer);
 125                return;
 126        }
 127
 128        image->ima_buffer_addr = kbuf.mem;
 129        image->ima_buffer_size = kexec_segment_size;
 130        image->ima_buffer = kexec_buffer;
 131
 132        pr_debug("kexec measurement buffer for the loaded kernel at 0x%lx.\n",
 133                 kbuf.mem);
 134}
 135#endif /* IMA_KEXEC */
 136
 137/*
 138 * Restore the measurement list from the previous kernel.
 139 */
 140void ima_load_kexec_buffer(void)
 141{
 142        void *kexec_buffer = NULL;
 143        size_t kexec_buffer_size = 0;
 144        int rc;
 145
 146        rc = ima_get_kexec_buffer(&kexec_buffer, &kexec_buffer_size);
 147        switch (rc) {
 148        case 0:
 149                rc = ima_restore_measurement_list(kexec_buffer_size,
 150                                                  kexec_buffer);
 151                if (rc != 0)
 152                        pr_err("Failed to restore the measurement list: %d\n",
 153                                rc);
 154
 155                ima_free_kexec_buffer();
 156                break;
 157        case -ENOTSUPP:
 158                pr_debug("Restoring the measurement list not supported\n");
 159                break;
 160        case -ENOENT:
 161                pr_debug("No measurement list to restore\n");
 162                break;
 163        default:
 164                pr_debug("Error restoring the measurement list: %d\n", rc);
 165        }
 166}
 167