efivar/0085-Add-security-types-guids-and-signature-database-iter.patch
Petr Šabata 974b936917 RHEL 9.0.0 Alpha bootstrap
The content of this branch was automatically imported from Fedora ELN
with the following as its source:
https://src.fedoraproject.org/rpms/efivar#9f54d8029387895b7b1389d9a9f9e0bf476a027f
2020-10-14 23:58:29 +02:00

461 lines
12 KiB
Diff

From 8c408cbc39878f9bf9ebcc9920a245c33a2defd0 Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Mon, 3 Feb 2020 13:25:40 -0500
Subject: [PATCH 85/86] Add security types/guids and signature database
iterators
Signed-off-by: Peter Jones <pjones@redhat.com>
---
src/Makefile | 2 +-
src/include/efivar/efisec-secdb.h | 61 ++++++
src/include/efivar/efisec.h | 1 +
src/libefisec.map.in | 4 +
src/secdb.c | 329 ++++++++++++++++++++++++++++++
5 files changed, 396 insertions(+), 1 deletion(-)
create mode 100644 src/include/efivar/efisec-secdb.h
create mode 100644 src/secdb.c
diff --git a/src/Makefile b/src/Makefile
index 883e058facf..a73f8f34ce9 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -13,7 +13,7 @@ PCTARGETS=efivar.pc efiboot.pc efisec.pc
TARGETS=$(LIBTARGETS) $(BINTARGETS) $(PCTARGETS)
STATICTARGETS=$(STATICLIBTARGETS) $(STATICBINTARGETS)
-LIBEFISEC_SOURCES = sec.c
+LIBEFISEC_SOURCES = sec.c secdb.c
LIBEFISEC_OBJECTS = $(patsubst %.c,%.o,$(LIBEFISEC_SOURCES))
LIBEFIBOOT_SOURCES = crc32.c creator.c disk.c gpt.c loadopt.c path-helpers.c \
linux.c $(sort $(wildcard linux-*.c))
diff --git a/src/include/efivar/efisec-secdb.h b/src/include/efivar/efisec-secdb.h
new file mode 100644
index 00000000000..0b7103a38d7
--- /dev/null
+++ b/src/include/efivar/efisec-secdb.h
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * Copyright 2014-2020 Red Hat, Inc.
+ * Copyright 2014-2020 Peter M. Jones <pjones@redhat.com>
+ *
+ * Author(s): Peter Jones <pjones@redhat.com>
+ */
+#ifndef EFISEC_SECDB_H_
+#define EFISEC_SECDB_H_ 1
+
+#include <stdint.h>
+#include <unistd.h>
+
+typedef struct efi_secdb_iter efi_secdb_iter;
+
+/*
+ * efi_secdb_iter_new - create a new iterator over a efi security database
+ * iter: pointer to a NULL efi_secdb_iter pointer.
+ * buf: security database from the file
+ * len: size of the file
+ *
+ * returns 0 on success, negative on error, sets errno.
+ */
+extern int efi_secdb_iter_new(efi_secdb_iter **iter, uint8_t *buf, size_t len)
+ __attribute__((__nonnull__(1, 2)));
+
+/*
+ * efi_secdb_iter_end - destroy the iterator created by efi_secdb_iter_new()
+ * iter: the iterator being destroyed
+ *
+ * returns 0 on success, negative on error, sets errno.
+ */
+extern int efi_secdb_iter_end(efi_secdb_iter *iter)
+ __attribute__((__nonnull__(1)));
+
+/*
+ * efi_secdb_iter_next - get the next item in the list
+ * iter: the iterator
+ * type: the type of the entry
+ * owner: the owner of the entry
+ * data: the identifying data
+ * len: the size of the data
+ *
+ * returns negative and sets errno on error,
+ * 0 if there weren't any entries (type/owner/data/len are not populated)
+ * 1 if an entry was returned.
+ */
+extern int efi_secdb_iter_next(efi_secdb_iter *iter, efi_guid_t *type,
+ efi_guid_t *owner, uint8_t **data, size_t *len)
+ __attribute__((__nonnull__(1, 2, 3, 4, 5)));
+
+/*
+ * efi_secdb_iter_get_line - tell how many entries have been returned
+ * iter: the iterator
+ *
+ * return value: -1 on error, with errno set, >=0 in all other cases
+ */
+extern int efi_secdb_iter_get_line(efi_secdb_iter *iter)
+ __attribute__((__nonnull__(1)));
+
+#endif /* EFISEC_SECDB_H_ */
diff --git a/src/include/efivar/efisec.h b/src/include/efivar/efisec.h
index f62bcedbf6f..2072e5c9149 100644
--- a/src/include/efivar/efisec.h
+++ b/src/include/efivar/efisec.h
@@ -10,6 +10,7 @@
#include <efivar/efivar.h>
#include <efivar/efisec-types.h>
+#include <efivar/efisec-secdb.h>
extern uint32_t efi_get_libefisec_version(void)
__attribute__((__visibility__("default")));
diff --git a/src/libefisec.map.in b/src/libefisec.map.in
index 2e732cf1d9b..50ae27df44a 100644
--- a/src/libefisec.map.in
+++ b/src/libefisec.map.in
@@ -4,4 +4,8 @@ libefisec.so.0 {
LIBEFISEC_1.38 {
global: efi_get_libefisec_version;
+ efi_secdb_iter_new;
+ efi_secdb_iter_end;
+ efi_secdb_iter_next;
+ efi_secdb_iter_get_line;
} libefisec.so.0;
diff --git a/src/secdb.c b/src/secdb.c
new file mode 100644
index 00000000000..e8ea0180cfd
--- /dev/null
+++ b/src/secdb.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * Copyright 2014-2020 Red Hat, Inc.
+ * Copyright 2014-2020 Peter M. Jones <pjones@redhat.com>
+ *
+ * Author(s): Peter Jones <pjones@redhat.com>
+ */
+
+#include "fix_coverity.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "efisec.h"
+
+typedef struct efi_secdb_list_iter efi_secdb_list_iter;
+extern int efi_secdb_list_iter_new(efi_secdb_list_iter **iter, uint8_t *buf, size_t len);
+extern int efi_secdb_list_iter_end(efi_secdb_list_iter *iter);
+extern int efi_secdb_list_iter_next(efi_secdb_list_iter *iter, efi_guid_t *type,
+ efi_signature_data_t **data, size_t *len);
+extern int efi_secdb_list_list_size(efi_secdb_list_iter *iter, size_t *sls);
+extern int efi_secdb_list_header_size(efi_secdb_list_iter *iter, size_t *slh);
+extern int efi_secdb_list_sig_size(efi_secdb_list_iter *iter, size_t *ss);
+extern int efi_secdb_list_get_type(efi_secdb_list_iter *iter, efi_guid_t *type);
+
+struct efi_secdb_iter {
+ efi_secdb_list_iter *iter;
+ int line;
+
+ efi_signature_data_t *esd;
+ size_t len;
+
+ size_t nmemb;
+ unsigned int i;
+};
+
+int NONNULL(1, 2) PUBLIC
+efi_secdb_iter_new(efi_secdb_iter **iter, uint8_t *buf, size_t len)
+{
+ int rc;
+
+ if (len < sizeof (efi_signature_list_t) + sizeof (efi_signature_data_t)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *iter = calloc(1, sizeof (efi_secdb_iter));
+ if (!*iter)
+ return -1;
+
+ rc = efi_secdb_list_iter_new(&(*iter)->iter, buf, len);
+ if (rc < 0) {
+ int error = errno;
+ free(*iter);
+ errno = error;
+ return -1;
+ }
+
+ (*iter)->i = -1;
+
+ return 0;
+}
+
+int NONNULL(1) PUBLIC
+efi_secdb_iter_end(efi_secdb_iter *iter)
+{
+ if (!iter) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (iter->iter)
+ efi_secdb_list_iter_end(iter->iter);
+ free(iter);
+ return 0;
+}
+
+int NONNULL(1, 2, 3, 4, 5) PUBLIC
+efi_secdb_iter_next(efi_secdb_iter *iter, efi_guid_t *type,
+ efi_guid_t *owner, uint8_t **data, size_t *len)
+{
+ int rc;
+ size_t ss;
+
+ if (!iter)
+ return -EINVAL;
+
+ if (iter->iter == NULL)
+ return -EINVAL;
+
+ iter->line += 1;
+
+ iter->i += 1;
+ if (iter->i == iter->nmemb) {
+ debug("Getting next efi_signature_data_t\n");
+ iter->i = 0;
+ rc = efi_secdb_list_iter_next(iter->iter, type, &iter->esd, &iter->len);
+ if (rc < 1)
+ return rc;
+
+ if (!efi_guid_cmp(type, &efi_guid_x509_cert)) {
+ int32_t asn1size;
+
+ asn1size = get_asn1_seq_size(iter->esd->signature_data,
+ iter->len - sizeof (iter->esd->signature_owner));
+
+ if (asn1size < 0) {
+ debug("iterator data claims to be an X.509 Cert but is not valid ASN.1 DER");
+ } else if ((uint32_t)asn1size != iter->len -
+ sizeof (iter->esd->signature_owner)) {
+ debug("X.509 Cert ASN.1 size does not match signature_List Size (%d vs %zu)",
+ asn1size, iter->len -
+ sizeof (iter->esd->signature_owner));
+ }
+ }
+
+ size_t sls, slh;
+ rc = efi_secdb_list_list_size(iter->iter, &sls);
+ if (rc < 0)
+ return rc;
+
+ rc = efi_secdb_list_header_size(iter->iter, &slh);
+ if (rc < 0)
+ return rc;
+
+ rc = efi_secdb_list_sig_size(iter->iter, &ss);
+ if (rc < 0)
+ return rc;
+
+ /* if we'd have leftover data, then this ESD is garbage. */
+ if ((sls - sizeof (efi_signature_list_t) - slh) % ss != 0)
+ return -EINVAL;
+
+ iter->nmemb = (sls - sizeof (efi_signature_list_t) - slh) / ss;
+ } else {
+ debug("Getting next esd element\n");
+ rc = efi_secdb_list_sig_size(iter->iter, &ss);
+ if (rc < 0)
+ return rc;
+
+ iter->esd = (efi_signature_data_t *)((intptr_t)iter->esd + ss);
+ }
+
+ rc = efi_secdb_list_get_type(iter->iter, type);
+ if (rc < 0)
+ return rc;
+
+ *owner = iter->esd->signature_owner;
+ *data = iter->esd->signature_data;
+ *len = ss - sizeof (iter->esd->signature_owner);
+ return 1;
+}
+
+int NONNULL(1) PUBLIC
+efi_secdb_iter_get_line(efi_secdb_iter *iter)
+{
+ if (!iter) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return iter->line;
+}
+
+struct efi_secdb_list_iter {
+ uint8_t *buf;
+ size_t len;
+
+ off_t offset;
+
+ efi_signature_list_t *esl;
+};
+
+int NONNULL(1, 2)
+efi_secdb_list_iter_new(efi_secdb_list_iter **iter, uint8_t *buf, size_t len)
+{
+ if (len < sizeof (efi_signature_list_t) + sizeof (efi_signature_data_t)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *iter = calloc(1, sizeof (efi_secdb_list_iter));
+ if (!*iter)
+ return -1;
+
+ (*iter)->buf = buf;
+ (*iter)->len = len;
+
+ return 0;
+}
+
+int NONNULL(1)
+efi_secdb_list_iter_end(efi_secdb_list_iter *iter)
+{
+ if (!iter) {
+ errno = EINVAL;
+ return -1;
+ }
+ free(iter);
+ return 0;
+}
+
+int NONNULL(1, 2, 3, 4)
+efi_secdb_list_iter_next(efi_secdb_list_iter *iter, efi_guid_t *type,
+ efi_signature_data_t **data, size_t *len)
+{
+ if (!iter)
+ return -EINVAL;
+ if (iter->offset < 0)
+ return -EINVAL;
+ if ((uint32_t)iter->offset >= iter->len)
+ return -EINVAL;
+
+ if (!iter->esl) {
+ debug("Getting next ESL buffer\n");
+ iter->esl = (efi_signature_list_t *)iter->buf;
+ } else {
+ debug("Getting next efi_signature_list_t\n");
+ efi_guid_t type;
+ efi_secdb_list_get_type(iter, &type);
+ if (iter->len - iter->offset < iter->esl->signature_list_size) {
+ debug("EFI signature_ List is malformed");
+ debug("list has %lu bytes left, element is %"PRIu32" bytes",
+ iter->len - iter->offset,
+ iter->esl->signature_list_size);
+ return -1;
+ }
+ if (!efi_guid_cmp(&type, &efi_guid_x509_cert)) {
+ int32_t asn1size;
+
+ asn1size = get_asn1_seq_size(
+ ((uint8_t *)*data) + sizeof (efi_guid_t),
+ *len - sizeof (efi_guid_t));
+ if (asn1size < 0) {
+ debug("iterator data claims to be an X.509 Cert but is not valid ASN.1 DER");
+ } else if ((uint32_t)asn1size != iter->esl->signature_size
+ - sizeof (efi_guid_t)) {
+ debug("X.509 Cert ASN.1 size does not match signature_List Size (%d vs %zu)",
+ asn1size, iter->esl->signature_size -
+ sizeof (efi_guid_t));
+ }
+
+ }
+
+ iter->offset += iter->esl->signature_list_size;
+ if ((uint32_t)iter->offset >= iter->len)
+ return 0;
+ iter->esl = (efi_signature_list_t *)((intptr_t)iter->buf
+ + iter->offset);
+ }
+
+ efi_signature_list_t esl;
+ memset(&esl, '\0', sizeof (esl));
+ /* if somehow we've gotten a buffer that's bigger than our
+ * real list, this will be zeros, so we've hit the end. */
+ if (!memcmp(&esl, iter->esl, sizeof (esl)))
+ return 0;
+
+ /* if this list size is too big for our data, then it's malformed
+ * data and we're done. */
+ if (iter->esl->signature_list_size > iter->len - iter->offset)
+ return -EINVAL;
+
+ *type = iter->esl->signature_type;
+ *data = (efi_signature_data_t *)((intptr_t)iter->esl
+ + sizeof (efi_signature_list_t)
+ + iter->esl->signature_header_size);
+ *len = iter->esl->signature_list_size - sizeof (efi_signature_list_t);
+
+ return 1;
+}
+
+int NONNULL(1, 2)
+efi_secdb_list_list_size(efi_secdb_list_iter *iter, size_t *sls)
+{
+ if (!iter || !iter->esl) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* this has to be at least as large as its header to be valid */
+ if (iter->esl->signature_list_size < sizeof (efi_signature_list_t)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *sls = iter->esl->signature_list_size;
+ return 0;
+}
+
+int NONNULL(1, 2)
+efi_secdb_list_header_size(efi_secdb_list_iter *iter, size_t *slh)
+{
+ if (!iter || !iter->esl) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *slh = iter->esl->signature_header_size;
+ return 0;
+}
+
+int NONNULL(1, 2)
+efi_secdb_list_sig_size(efi_secdb_list_iter *iter, size_t *ss)
+{
+ if (!iter || !iter->esl) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* If signature size isn't positive, there's invalid data. */
+ if (iter->esl->signature_size < 1) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ *ss = iter->esl->signature_size;
+ return 0;
+}
+
+int NONNULL(1, 2)
+efi_secdb_list_get_type(efi_secdb_list_iter *iter, efi_guid_t *type)
+{
+ if (!iter || !iter->esl) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memcpy(type, &iter->esl->signature_type, sizeof (*type));
+ return 0;
+}
--
2.24.1