Blob Blame History Raw
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