1/*-
2 * Copyright (c) 2019 Stormshield.
3 * Copyright (c) 2019 Semihalf.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <stand.h>
33#include <string.h>
34
35#include <efi.h>
36#include <efilib.h>
37#include <Guid/ImageAuthentication.h>
38
39#define NEED_BRSSL_H
40#include "../libsecureboot-priv.h"
41#include <brssl.h>
42
43static EFI_GUID ImageSecurityDatabaseGUID = EFI_IMAGE_SECURITY_DATABASE_GUID;
44
45static EFI_GUID efiCertX509GUID = EFI_CERT_X509_GUID;
46static EFI_GUID efiCertX509Sha256GUID = EFI_CERT_X509_SHA256_GUID;
47static EFI_GUID efiCertX509Sha384GUID = EFI_CERT_X509_SHA384_GUID;
48static EFI_GUID efiCertX509Sha5122UID = EFI_CERT_X509_SHA512_GUID;
49
50/*
51 * Check if Secure Boot is enabled in firmware.
52 * We evaluate two variables - Secure Boot and Setup Mode.
53 * Secure Boot is enforced only if the first one equals 1 and the other 0.
54 */
55int
56efi_secure_boot_enabled(void)
57{
58	UINT8 SecureBoot;
59	UINT8 SetupMode;
60	size_t length;
61	EFI_STATUS status;
62
63	length = sizeof(SecureBoot);
64	status = efi_global_getenv("SecureBoot", &SecureBoot, &length);
65	if (status != EFI_SUCCESS) {
66		if (status == EFI_NOT_FOUND)
67			return (0);
68
69		printf("Failed to read \"SecureBoot\" variable\n");
70		return (-efi_status_to_errno(status));
71	}
72
73	length = sizeof(SetupMode);
74	status = efi_global_getenv("SetupMode", &SetupMode, &length);
75	if (status != EFI_SUCCESS)
76		SetupMode = 0;
77
78	printf("   SecureBoot: %d, SetupMode: %d\n", SecureBoot, SetupMode);
79
80	return (SecureBoot == 1 && SetupMode == 0);
81}
82
83/*
84 * Iterate through UEFI variable and extract X509 certificates from it.
85 * The EFI_* structures and related guids are defined in UEFI standard.
86 */
87static br_x509_certificate*
88efi_get_certs(const char *name, size_t *count)
89{
90	br_x509_certificate *certs;
91	UINT8 *database;
92	EFI_SIGNATURE_LIST *list;
93	EFI_SIGNATURE_DATA *entry;
94	size_t db_size;
95	ssize_t cert_count;
96	EFI_STATUS status;
97
98	database = NULL;
99	certs = NULL;
100	db_size = 0;
101	cert_count = 0;
102
103	/*
104	 * Read variable length and allocate memory for it
105	 */
106	status = efi_getenv(&ImageSecurityDatabaseGUID, name, database, &db_size);
107	if (status != EFI_BUFFER_TOO_SMALL)
108		return (NULL);
109
110	database = malloc(db_size);
111	if (database == NULL)
112		return (NULL);
113
114	status = efi_getenv(&ImageSecurityDatabaseGUID, name, database, &db_size);
115	if (status != EFI_SUCCESS)
116		goto fail;
117
118	for (list = (EFI_SIGNATURE_LIST*) database;
119	    db_size >= list->SignatureListSize && db_size > 0;
120	    db_size -= list->SignatureListSize,
121	    list = (EFI_SIGNATURE_LIST*)
122	    ((UINT8*)list + list->SignatureListSize)) {
123
124		/* We are only interested in entries containing X509 certs. */
125		if (memcmp(&efiCertX509GUID,
126		    &list->SignatureType,
127		    sizeof(EFI_GUID)) != 0) {
128			continue;
129		}
130
131		entry = (EFI_SIGNATURE_DATA*)
132		    ((UINT8*)list +
133		    sizeof(EFI_SIGNATURE_LIST) +
134		    list->SignatureHeaderSize);
135
136		certs = realloc(certs,
137		    (cert_count + 1) * sizeof(br_x509_certificate));
138		if (certs == NULL) {
139			cert_count = 0;
140			goto fail;
141		}
142
143		certs[cert_count].data_len = list->SignatureSize - sizeof(EFI_GUID);
144		certs[cert_count].data = malloc(certs[cert_count].data_len);
145		if (certs[cert_count].data == NULL)
146			goto fail;
147
148		memcpy(certs[cert_count].data,
149		    entry->SignatureData,
150		    certs[cert_count].data_len);
151
152		cert_count++;
153	}
154
155	*count = cert_count;
156
157	xfree(database);
158	return (certs);
159
160fail:
161	free_certificates(certs, cert_count);
162	xfree(database);
163	return (NULL);
164
165}
166
167/*
168 * Extract digests from UEFI "dbx" variable.
169 * UEFI standard specifies three types of digest - sha256, sha386, sha512.
170 */
171hash_data*
172efi_get_forbidden_digests(size_t *count)
173{
174	UINT8 *database;
175	hash_data *digests;
176	EFI_SIGNATURE_LIST *list;
177	EFI_SIGNATURE_DATA *entry;
178	size_t db_size, header_size, hash_size;
179	int digest_count, entry_count;
180	EFI_STATUS status;
181
182	db_size = 0;
183	digest_count = 0;
184	database = NULL;
185	digests = NULL;
186
187	status = efi_getenv(&ImageSecurityDatabaseGUID, "dbx", database, &db_size);
188	if (status != EFI_BUFFER_TOO_SMALL)
189		return (NULL);
190
191	database = malloc(db_size);
192	if (database == NULL)
193		return (NULL);
194
195	status = efi_getenv(&ImageSecurityDatabaseGUID, "dbx", database, &db_size);
196	if (status != EFI_SUCCESS)
197		goto fail;
198
199
200	for (list = (EFI_SIGNATURE_LIST*) database;
201	    db_size >= list->SignatureListSize && db_size > 0;
202	    db_size -= list->SignatureListSize,
203	    list = (EFI_SIGNATURE_LIST*)
204	    ((UINT8*)list + list->SignatureListSize)) {
205
206		/* We are only interested in entries that contain digests. */
207		if (memcmp(&efiCertX509Sha256GUID, &list->SignatureType,
208		    sizeof(EFI_GUID)) == 0) {
209			hash_size = br_sha256_SIZE;
210		} else if (memcmp(&efiCertX509Sha384GUID, &list->SignatureType,
211		    sizeof(EFI_GUID)) == 0) {
212			hash_size = br_sha384_SIZE;
213		} else if (memcmp(&efiCertX509Sha5122UID, &list->SignatureType,
214		    sizeof(EFI_GUID)) == 0) {
215			hash_size = br_sha512_SIZE;
216		} else {
217			continue;
218		}
219
220		/*
221		 * A single entry can have multiple digests
222		 * of the same type for some reason.
223		 */
224		header_size = sizeof(EFI_SIGNATURE_LIST) + list->SignatureHeaderSize;
225
226		/* Calculate the number of entries basing on structure size */
227		entry_count = list->SignatureListSize - header_size;
228		entry_count /= list->SignatureSize;
229		entry = (EFI_SIGNATURE_DATA*)((UINT8*)list + header_size);
230		while (entry_count-- > 0) {
231			digests = realloc(digests,
232			    (digest_count + 1) * sizeof(hash_data));
233			if (digests == NULL) {
234				digest_count = 0;
235				goto fail;
236			}
237
238			digests[digest_count].data = malloc(hash_size);
239			if (digests[digest_count].data == NULL)
240				goto fail;
241
242			memcpy(digests[digest_count].data,
243			    entry->SignatureData,
244			    hash_size);
245			digests[digest_count].hash_size = hash_size;
246
247			entry = (EFI_SIGNATURE_DATA*)(entry + list->SignatureSize);
248			digest_count++;
249		}
250	}
251	xfree(database);
252	if (count != NULL)
253		*count = digest_count;
254
255	return (digests);
256
257fail:
258	while (digest_count--)
259		xfree(digests[digest_count].data);
260
261	xfree(database);
262	xfree(digests);
263	return (NULL);
264}
265
266/* Copy x509 certificates from db */
267br_x509_certificate*
268efi_get_trusted_certs(size_t *count)
269{
270	return (efi_get_certs("db", count));
271}
272
273/* Copy forbidden certificates from dbx */
274br_x509_certificate*
275efi_get_forbidden_certs(size_t *count)
276{
277	return (efi_get_certs("dbx", count));
278}
279