18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/* EFI signature/key/certificate list parser
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2012, 2016 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "EFI: "fmt
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/printk.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/efi.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/**
158c2ecf20Sopenharmony_ci * parse_efi_signature_list - Parse an EFI signature list for certificates
168c2ecf20Sopenharmony_ci * @source: The source of the key
178c2ecf20Sopenharmony_ci * @data: The data blob to parse
188c2ecf20Sopenharmony_ci * @size: The size of the data blob
198c2ecf20Sopenharmony_ci * @get_handler_for_guid: Get the handler func for the sig type (or NULL)
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * Parse an EFI signature list looking for elements of interest.  A list is
228c2ecf20Sopenharmony_ci * made up of a series of sublists, where all the elements in a sublist are of
238c2ecf20Sopenharmony_ci * the same type, but sublists can be of different types.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * For each sublist encountered, the @get_handler_for_guid function is called
268c2ecf20Sopenharmony_ci * with the type specifier GUID and returns either a pointer to a function to
278c2ecf20Sopenharmony_ci * handle elements of that type or NULL if the type is not of interest.
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * If the sublist is of interest, each element is passed to the handler
308c2ecf20Sopenharmony_ci * function in turn.
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * Error EBADMSG is returned if the list doesn't parse correctly and 0 is
338c2ecf20Sopenharmony_ci * returned if the list was parsed correctly.  No error can be returned from
348c2ecf20Sopenharmony_ci * the @get_handler_for_guid function or the element handler function it
358c2ecf20Sopenharmony_ci * returns.
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ciint __init parse_efi_signature_list(
388c2ecf20Sopenharmony_ci	const char *source,
398c2ecf20Sopenharmony_ci	const void *data, size_t size,
408c2ecf20Sopenharmony_ci	efi_element_handler_t (*get_handler_for_guid)(const efi_guid_t *))
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	efi_element_handler_t handler;
438c2ecf20Sopenharmony_ci	unsigned int offs = 0;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	pr_devel("-->%s(,%zu)\n", __func__, size);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	while (size > 0) {
488c2ecf20Sopenharmony_ci		const efi_signature_data_t *elem;
498c2ecf20Sopenharmony_ci		efi_signature_list_t list;
508c2ecf20Sopenharmony_ci		size_t lsize, esize, hsize, elsize;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci		if (size < sizeof(list))
538c2ecf20Sopenharmony_ci			return -EBADMSG;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		memcpy(&list, data, sizeof(list));
568c2ecf20Sopenharmony_ci		pr_devel("LIST[%04x] guid=%pUl ls=%x hs=%x ss=%x\n",
578c2ecf20Sopenharmony_ci			 offs,
588c2ecf20Sopenharmony_ci			 list.signature_type.b, list.signature_list_size,
598c2ecf20Sopenharmony_ci			 list.signature_header_size, list.signature_size);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci		lsize = list.signature_list_size;
628c2ecf20Sopenharmony_ci		hsize = list.signature_header_size;
638c2ecf20Sopenharmony_ci		esize = list.signature_size;
648c2ecf20Sopenharmony_ci		elsize = lsize - sizeof(list) - hsize;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		if (lsize > size) {
678c2ecf20Sopenharmony_ci			pr_devel("<--%s() = -EBADMSG [overrun @%x]\n",
688c2ecf20Sopenharmony_ci				 __func__, offs);
698c2ecf20Sopenharmony_ci			return -EBADMSG;
708c2ecf20Sopenharmony_ci		}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci		if (lsize < sizeof(list) ||
738c2ecf20Sopenharmony_ci		    lsize - sizeof(list) < hsize ||
748c2ecf20Sopenharmony_ci		    esize < sizeof(*elem) ||
758c2ecf20Sopenharmony_ci		    elsize < esize ||
768c2ecf20Sopenharmony_ci		    elsize % esize != 0) {
778c2ecf20Sopenharmony_ci			pr_devel("- bad size combo @%x\n", offs);
788c2ecf20Sopenharmony_ci			return -EBADMSG;
798c2ecf20Sopenharmony_ci		}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci		handler = get_handler_for_guid(&list.signature_type);
828c2ecf20Sopenharmony_ci		if (!handler) {
838c2ecf20Sopenharmony_ci			data += lsize;
848c2ecf20Sopenharmony_ci			size -= lsize;
858c2ecf20Sopenharmony_ci			offs += lsize;
868c2ecf20Sopenharmony_ci			continue;
878c2ecf20Sopenharmony_ci		}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		data += sizeof(list) + hsize;
908c2ecf20Sopenharmony_ci		size -= sizeof(list) + hsize;
918c2ecf20Sopenharmony_ci		offs += sizeof(list) + hsize;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		for (; elsize > 0; elsize -= esize) {
948c2ecf20Sopenharmony_ci			elem = data;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci			pr_devel("ELEM[%04x]\n", offs);
978c2ecf20Sopenharmony_ci			handler(source,
988c2ecf20Sopenharmony_ci				&elem->signature_data,
998c2ecf20Sopenharmony_ci				esize - sizeof(*elem));
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci			data += esize;
1028c2ecf20Sopenharmony_ci			size -= esize;
1038c2ecf20Sopenharmony_ci			offs += esize;
1048c2ecf20Sopenharmony_ci		}
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
109