18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#include <linux/kernel.h>
48c2ecf20Sopenharmony_ci#include <linux/sched.h>
58c2ecf20Sopenharmony_ci#include <linux/cred.h>
68c2ecf20Sopenharmony_ci#include <linux/dmi.h>
78c2ecf20Sopenharmony_ci#include <linux/err.h>
88c2ecf20Sopenharmony_ci#include <linux/efi.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <keys/asymmetric-type.h>
118c2ecf20Sopenharmony_ci#include <keys/system_keyring.h>
128c2ecf20Sopenharmony_ci#include "../integrity.h"
138c2ecf20Sopenharmony_ci#include "keyring_handler.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/*
168c2ecf20Sopenharmony_ci * On T2 Macs reading the db and dbx efi variables to load UEFI Secure Boot
178c2ecf20Sopenharmony_ci * certificates causes occurrence of a page fault in Apple's firmware and
188c2ecf20Sopenharmony_ci * a crash disabling EFI runtime services. The following quirk skips reading
198c2ecf20Sopenharmony_ci * these variables.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_cistatic const struct dmi_system_id uefi_skip_cert[] = {
228c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,1") },
238c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,2") },
248c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,3") },
258c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro15,4") },
268c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,1") },
278c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,2") },
288c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,3") },
298c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookPro16,4") },
308c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir8,1") },
318c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir8,2") },
328c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacBookAir9,1") },
338c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "Macmini8,1") },
348c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "MacPro7,1") },
358c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMac20,1") },
368c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMac20,2") },
378c2ecf20Sopenharmony_ci	{ UEFI_QUIRK_SKIP_CERT("Apple Inc.", "iMacPro1,1") },
388c2ecf20Sopenharmony_ci	{ }
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/*
428c2ecf20Sopenharmony_ci * Look to see if a UEFI variable called MokIgnoreDB exists and return true if
438c2ecf20Sopenharmony_ci * it does.
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * This UEFI variable is set by the shim if a user tells the shim to not use
468c2ecf20Sopenharmony_ci * the certs/hashes in the UEFI db variable for verification purposes.  If it
478c2ecf20Sopenharmony_ci * is set, we should ignore the db variable also and the true return indicates
488c2ecf20Sopenharmony_ci * this.
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_cistatic __init bool uefi_check_ignore_db(void)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	efi_status_t status;
538c2ecf20Sopenharmony_ci	unsigned int db = 0;
548c2ecf20Sopenharmony_ci	unsigned long size = sizeof(db);
558c2ecf20Sopenharmony_ci	efi_guid_t guid = EFI_SHIM_LOCK_GUID;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	status = efi.get_variable(L"MokIgnoreDB", &guid, NULL, &size, &db);
588c2ecf20Sopenharmony_ci	return status == EFI_SUCCESS;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci * Get a certificate list blob from the named EFI variable.
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistatic __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid,
658c2ecf20Sopenharmony_ci				  unsigned long *size, efi_status_t *status)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	unsigned long lsize = 4;
688c2ecf20Sopenharmony_ci	unsigned long tmpdb[4];
698c2ecf20Sopenharmony_ci	void *db;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	*status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb);
728c2ecf20Sopenharmony_ci	if (*status == EFI_NOT_FOUND)
738c2ecf20Sopenharmony_ci		return NULL;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (*status != EFI_BUFFER_TOO_SMALL) {
768c2ecf20Sopenharmony_ci		pr_err("Couldn't get size: 0x%lx\n", *status);
778c2ecf20Sopenharmony_ci		return NULL;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	db = kmalloc(lsize, GFP_KERNEL);
818c2ecf20Sopenharmony_ci	if (!db)
828c2ecf20Sopenharmony_ci		return NULL;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	*status = efi.get_variable(name, guid, NULL, &lsize, db);
858c2ecf20Sopenharmony_ci	if (*status != EFI_SUCCESS) {
868c2ecf20Sopenharmony_ci		kfree(db);
878c2ecf20Sopenharmony_ci		pr_err("Error reading db var: 0x%lx\n", *status);
888c2ecf20Sopenharmony_ci		return NULL;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	*size = lsize;
928c2ecf20Sopenharmony_ci	return db;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/*
968c2ecf20Sopenharmony_ci * load_moklist_certs() - Load MokList certs
978c2ecf20Sopenharmony_ci *
988c2ecf20Sopenharmony_ci * Load the certs contained in the UEFI MokListRT database into the
998c2ecf20Sopenharmony_ci * platform trusted keyring.
1008c2ecf20Sopenharmony_ci *
1018c2ecf20Sopenharmony_ci * This routine checks the EFI MOK config table first. If and only if
1028c2ecf20Sopenharmony_ci * that fails, this routine uses the MokListRT ordinary UEFI variable.
1038c2ecf20Sopenharmony_ci *
1048c2ecf20Sopenharmony_ci * Return:	Status
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_cistatic int __init load_moklist_certs(void)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct efi_mokvar_table_entry *mokvar_entry;
1098c2ecf20Sopenharmony_ci	efi_guid_t mok_var = EFI_SHIM_LOCK_GUID;
1108c2ecf20Sopenharmony_ci	void *mok;
1118c2ecf20Sopenharmony_ci	unsigned long moksize;
1128c2ecf20Sopenharmony_ci	efi_status_t status;
1138c2ecf20Sopenharmony_ci	int rc;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* First try to load certs from the EFI MOKvar config table.
1168c2ecf20Sopenharmony_ci	 * It's not an error if the MOKvar config table doesn't exist
1178c2ecf20Sopenharmony_ci	 * or the MokListRT entry is not found in it.
1188c2ecf20Sopenharmony_ci	 */
1198c2ecf20Sopenharmony_ci	mokvar_entry = efi_mokvar_entry_find("MokListRT");
1208c2ecf20Sopenharmony_ci	if (mokvar_entry) {
1218c2ecf20Sopenharmony_ci		rc = parse_efi_signature_list("UEFI:MokListRT (MOKvar table)",
1228c2ecf20Sopenharmony_ci					      mokvar_entry->data,
1238c2ecf20Sopenharmony_ci					      mokvar_entry->data_size,
1248c2ecf20Sopenharmony_ci					      get_handler_for_db);
1258c2ecf20Sopenharmony_ci		/* All done if that worked. */
1268c2ecf20Sopenharmony_ci		if (!rc)
1278c2ecf20Sopenharmony_ci			return rc;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		pr_err("Couldn't parse MokListRT signatures from EFI MOKvar config table: %d\n",
1308c2ecf20Sopenharmony_ci		       rc);
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* Get MokListRT. It might not exist, so it isn't an error
1348c2ecf20Sopenharmony_ci	 * if we can't get it.
1358c2ecf20Sopenharmony_ci	 */
1368c2ecf20Sopenharmony_ci	mok = get_cert_list(L"MokListRT", &mok_var, &moksize, &status);
1378c2ecf20Sopenharmony_ci	if (mok) {
1388c2ecf20Sopenharmony_ci		rc = parse_efi_signature_list("UEFI:MokListRT",
1398c2ecf20Sopenharmony_ci					      mok, moksize, get_handler_for_db);
1408c2ecf20Sopenharmony_ci		kfree(mok);
1418c2ecf20Sopenharmony_ci		if (rc)
1428c2ecf20Sopenharmony_ci			pr_err("Couldn't parse MokListRT signatures: %d\n", rc);
1438c2ecf20Sopenharmony_ci		return rc;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci	if (status == EFI_NOT_FOUND)
1468c2ecf20Sopenharmony_ci		pr_debug("MokListRT variable wasn't found\n");
1478c2ecf20Sopenharmony_ci	else
1488c2ecf20Sopenharmony_ci		pr_info("Couldn't get UEFI MokListRT\n");
1498c2ecf20Sopenharmony_ci	return 0;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/*
1538c2ecf20Sopenharmony_ci * load_uefi_certs() - Load certs from UEFI sources
1548c2ecf20Sopenharmony_ci *
1558c2ecf20Sopenharmony_ci * Load the certs contained in the UEFI databases into the platform trusted
1568c2ecf20Sopenharmony_ci * keyring and the UEFI blacklisted X.509 cert SHA256 hashes into the blacklist
1578c2ecf20Sopenharmony_ci * keyring.
1588c2ecf20Sopenharmony_ci */
1598c2ecf20Sopenharmony_cistatic int __init load_uefi_certs(void)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
1628c2ecf20Sopenharmony_ci	efi_guid_t mok_var = EFI_SHIM_LOCK_GUID;
1638c2ecf20Sopenharmony_ci	void *db = NULL, *dbx = NULL, *mokx = NULL;
1648c2ecf20Sopenharmony_ci	unsigned long dbsize = 0, dbxsize = 0, mokxsize = 0;
1658c2ecf20Sopenharmony_ci	efi_status_t status;
1668c2ecf20Sopenharmony_ci	int rc = 0;
1678c2ecf20Sopenharmony_ci	const struct dmi_system_id *dmi_id;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	dmi_id = dmi_first_match(uefi_skip_cert);
1708c2ecf20Sopenharmony_ci	if (dmi_id) {
1718c2ecf20Sopenharmony_ci		pr_err("Reading UEFI Secure Boot Certs is not supported on T2 Macs.\n");
1728c2ecf20Sopenharmony_ci		return false;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
1768c2ecf20Sopenharmony_ci		return false;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* Get db and dbx.  They might not exist, so it isn't an error
1798c2ecf20Sopenharmony_ci	 * if we can't get them.
1808c2ecf20Sopenharmony_ci	 */
1818c2ecf20Sopenharmony_ci	if (!uefi_check_ignore_db()) {
1828c2ecf20Sopenharmony_ci		db = get_cert_list(L"db", &secure_var, &dbsize, &status);
1838c2ecf20Sopenharmony_ci		if (!db) {
1848c2ecf20Sopenharmony_ci			if (status == EFI_NOT_FOUND)
1858c2ecf20Sopenharmony_ci				pr_debug("MODSIGN: db variable wasn't found\n");
1868c2ecf20Sopenharmony_ci			else
1878c2ecf20Sopenharmony_ci				pr_err("MODSIGN: Couldn't get UEFI db list\n");
1888c2ecf20Sopenharmony_ci		} else {
1898c2ecf20Sopenharmony_ci			rc = parse_efi_signature_list("UEFI:db",
1908c2ecf20Sopenharmony_ci					db, dbsize, get_handler_for_db);
1918c2ecf20Sopenharmony_ci			if (rc)
1928c2ecf20Sopenharmony_ci				pr_err("Couldn't parse db signatures: %d\n",
1938c2ecf20Sopenharmony_ci				       rc);
1948c2ecf20Sopenharmony_ci			kfree(db);
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	dbx = get_cert_list(L"dbx", &secure_var, &dbxsize, &status);
1998c2ecf20Sopenharmony_ci	if (!dbx) {
2008c2ecf20Sopenharmony_ci		if (status == EFI_NOT_FOUND)
2018c2ecf20Sopenharmony_ci			pr_debug("dbx variable wasn't found\n");
2028c2ecf20Sopenharmony_ci		else
2038c2ecf20Sopenharmony_ci			pr_info("Couldn't get UEFI dbx list\n");
2048c2ecf20Sopenharmony_ci	} else {
2058c2ecf20Sopenharmony_ci		rc = parse_efi_signature_list("UEFI:dbx",
2068c2ecf20Sopenharmony_ci					      dbx, dbxsize,
2078c2ecf20Sopenharmony_ci					      get_handler_for_dbx);
2088c2ecf20Sopenharmony_ci		if (rc)
2098c2ecf20Sopenharmony_ci			pr_err("Couldn't parse dbx signatures: %d\n", rc);
2108c2ecf20Sopenharmony_ci		kfree(dbx);
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	mokx = get_cert_list(L"MokListXRT", &mok_var, &mokxsize, &status);
2148c2ecf20Sopenharmony_ci	if (!mokx) {
2158c2ecf20Sopenharmony_ci		if (status == EFI_NOT_FOUND)
2168c2ecf20Sopenharmony_ci			pr_debug("mokx variable wasn't found\n");
2178c2ecf20Sopenharmony_ci		else
2188c2ecf20Sopenharmony_ci			pr_info("Couldn't get mokx list\n");
2198c2ecf20Sopenharmony_ci	} else {
2208c2ecf20Sopenharmony_ci		rc = parse_efi_signature_list("UEFI:MokListXRT",
2218c2ecf20Sopenharmony_ci					      mokx, mokxsize,
2228c2ecf20Sopenharmony_ci					      get_handler_for_dbx);
2238c2ecf20Sopenharmony_ci		if (rc)
2248c2ecf20Sopenharmony_ci			pr_err("Couldn't parse mokx signatures %d\n", rc);
2258c2ecf20Sopenharmony_ci		kfree(mokx);
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* Load the MokListRT certs */
2298c2ecf20Sopenharmony_ci	rc = load_moklist_certs();
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	return rc;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_cilate_initcall(load_uefi_certs);
234