974b936917
The content of this branch was automatically imported from Fedora ELN with the following as its source: https://src.fedoraproject.org/rpms/efivar#9f54d8029387895b7b1389d9a9f9e0bf476a027f
461 lines
12 KiB
Diff
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
|
|
|