1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #include <linux/kernel.h> |
4 | #include <linux/sched.h> |
5 | #include <linux/cred.h> |
6 | #include <linux/dmi.h> |
7 | #include <linux/err.h> |
8 | #include <linux/efi.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/ima.h> |
11 | #include <keys/asymmetric-type.h> |
12 | #include <keys/system_keyring.h> |
13 | #include "../integrity.h" |
14 | #include "keyring_handler.h" |
15 | |
16 | /* |
17 | * On T2 Macs reading the db and dbx efi variables to load UEFI Secure Boot |
18 | * certificates causes occurrence of a page fault in Apple's firmware and |
19 | * a crash disabling EFI runtime services. The following quirk skips reading |
20 | * these variables. |
21 | */ |
22 | static const struct dmi_system_id uefi_skip_cert[] = { |
23 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "MacBookPro15,1" ) }, |
24 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "MacBookPro15,2" ) }, |
25 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "MacBookPro15,3" ) }, |
26 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "MacBookPro15,4" ) }, |
27 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "MacBookPro16,1" ) }, |
28 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "MacBookPro16,2" ) }, |
29 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "MacBookPro16,3" ) }, |
30 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "MacBookPro16,4" ) }, |
31 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "MacBookAir8,1" ) }, |
32 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "MacBookAir8,2" ) }, |
33 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "MacBookAir9,1" ) }, |
34 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "Macmini8,1" ) }, |
35 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "MacPro7,1" ) }, |
36 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "iMac20,1" ) }, |
37 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "iMac20,2" ) }, |
38 | { UEFI_QUIRK_SKIP_CERT("Apple Inc." , "iMacPro1,1" ) }, |
39 | { } |
40 | }; |
41 | |
42 | /* |
43 | * Look to see if a UEFI variable called MokIgnoreDB exists and return true if |
44 | * it does. |
45 | * |
46 | * This UEFI variable is set by the shim if a user tells the shim to not use |
47 | * the certs/hashes in the UEFI db variable for verification purposes. If it |
48 | * is set, we should ignore the db variable also and the true return indicates |
49 | * this. |
50 | */ |
51 | static __init bool uefi_check_ignore_db(void) |
52 | { |
53 | efi_status_t status; |
54 | unsigned int db = 0; |
55 | unsigned long size = sizeof(db); |
56 | efi_guid_t guid = EFI_SHIM_LOCK_GUID; |
57 | |
58 | status = efi.get_variable(L"MokIgnoreDB" , &guid, NULL, &size, &db); |
59 | return status == EFI_SUCCESS; |
60 | } |
61 | |
62 | /* |
63 | * Get a certificate list blob from the named EFI variable. |
64 | */ |
65 | static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, |
66 | unsigned long *size, efi_status_t *status) |
67 | { |
68 | unsigned long lsize = 4; |
69 | unsigned long tmpdb[4]; |
70 | void *db; |
71 | |
72 | *status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb); |
73 | if (*status == EFI_NOT_FOUND) |
74 | return NULL; |
75 | |
76 | if (*status != EFI_BUFFER_TOO_SMALL) { |
77 | pr_err("Couldn't get size: 0x%lx\n" , *status); |
78 | return NULL; |
79 | } |
80 | |
81 | db = kmalloc(size: lsize, GFP_KERNEL); |
82 | if (!db) |
83 | return NULL; |
84 | |
85 | *status = efi.get_variable(name, guid, NULL, &lsize, db); |
86 | if (*status != EFI_SUCCESS) { |
87 | kfree(objp: db); |
88 | pr_err("Error reading db var: 0x%lx\n" , *status); |
89 | return NULL; |
90 | } |
91 | |
92 | *size = lsize; |
93 | return db; |
94 | } |
95 | |
96 | /* |
97 | * load_moklist_certs() - Load MokList certs |
98 | * |
99 | * Load the certs contained in the UEFI MokListRT database into the |
100 | * platform trusted keyring. |
101 | * |
102 | * This routine checks the EFI MOK config table first. If and only if |
103 | * that fails, this routine uses the MokListRT ordinary UEFI variable. |
104 | * |
105 | * Return: Status |
106 | */ |
107 | static int __init load_moklist_certs(void) |
108 | { |
109 | struct efi_mokvar_table_entry *mokvar_entry; |
110 | efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; |
111 | void *mok; |
112 | unsigned long moksize; |
113 | efi_status_t status; |
114 | int rc; |
115 | |
116 | /* First try to load certs from the EFI MOKvar config table. |
117 | * It's not an error if the MOKvar config table doesn't exist |
118 | * or the MokListRT entry is not found in it. |
119 | */ |
120 | mokvar_entry = efi_mokvar_entry_find(name: "MokListRT" ); |
121 | if (mokvar_entry) { |
122 | rc = parse_efi_signature_list(source: "UEFI:MokListRT (MOKvar table)" , |
123 | data: mokvar_entry->data, |
124 | size: mokvar_entry->data_size, |
125 | get_handler_for_guid: get_handler_for_mok); |
126 | /* All done if that worked. */ |
127 | if (!rc) |
128 | return rc; |
129 | |
130 | pr_err("Couldn't parse MokListRT signatures from EFI MOKvar config table: %d\n" , |
131 | rc); |
132 | } |
133 | |
134 | /* Get MokListRT. It might not exist, so it isn't an error |
135 | * if we can't get it. |
136 | */ |
137 | mok = get_cert_list(name: L"MokListRT" , guid: &mok_var, size: &moksize, status: &status); |
138 | if (mok) { |
139 | rc = parse_efi_signature_list(source: "UEFI:MokListRT" , |
140 | data: mok, size: moksize, get_handler_for_guid: get_handler_for_mok); |
141 | kfree(objp: mok); |
142 | if (rc) |
143 | pr_err("Couldn't parse MokListRT signatures: %d\n" , rc); |
144 | return rc; |
145 | } |
146 | if (status == EFI_NOT_FOUND) |
147 | pr_debug("MokListRT variable wasn't found\n" ); |
148 | else |
149 | pr_info("Couldn't get UEFI MokListRT\n" ); |
150 | return 0; |
151 | } |
152 | |
153 | /* |
154 | * load_uefi_certs() - Load certs from UEFI sources |
155 | * |
156 | * Load the certs contained in the UEFI databases into the platform trusted |
157 | * keyring and the UEFI blacklisted X.509 cert SHA256 hashes into the blacklist |
158 | * keyring. |
159 | */ |
160 | static int __init load_uefi_certs(void) |
161 | { |
162 | efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; |
163 | efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; |
164 | void *db = NULL, *dbx = NULL, *mokx = NULL; |
165 | unsigned long dbsize = 0, dbxsize = 0, mokxsize = 0; |
166 | efi_status_t status; |
167 | int rc = 0; |
168 | const struct dmi_system_id *dmi_id; |
169 | |
170 | dmi_id = dmi_first_match(list: uefi_skip_cert); |
171 | if (dmi_id) { |
172 | pr_err("Reading UEFI Secure Boot Certs is not supported on T2 Macs.\n" ); |
173 | return false; |
174 | } |
175 | |
176 | if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) |
177 | return false; |
178 | |
179 | /* Get db and dbx. They might not exist, so it isn't an error |
180 | * if we can't get them. |
181 | */ |
182 | if (!uefi_check_ignore_db()) { |
183 | db = get_cert_list(name: L"db" , guid: &secure_var, size: &dbsize, status: &status); |
184 | if (!db) { |
185 | if (status == EFI_NOT_FOUND) |
186 | pr_debug("MODSIGN: db variable wasn't found\n" ); |
187 | else |
188 | pr_err("MODSIGN: Couldn't get UEFI db list\n" ); |
189 | } else { |
190 | rc = parse_efi_signature_list(source: "UEFI:db" , |
191 | data: db, size: dbsize, get_handler_for_guid: get_handler_for_db); |
192 | if (rc) |
193 | pr_err("Couldn't parse db signatures: %d\n" , |
194 | rc); |
195 | kfree(objp: db); |
196 | } |
197 | } |
198 | |
199 | dbx = get_cert_list(name: L"dbx" , guid: &secure_var, size: &dbxsize, status: &status); |
200 | if (!dbx) { |
201 | if (status == EFI_NOT_FOUND) |
202 | pr_debug("dbx variable wasn't found\n" ); |
203 | else |
204 | pr_info("Couldn't get UEFI dbx list\n" ); |
205 | } else { |
206 | rc = parse_efi_signature_list(source: "UEFI:dbx" , |
207 | data: dbx, size: dbxsize, |
208 | get_handler_for_guid: get_handler_for_dbx); |
209 | if (rc) |
210 | pr_err("Couldn't parse dbx signatures: %d\n" , rc); |
211 | kfree(objp: dbx); |
212 | } |
213 | |
214 | /* the MOK/MOKx can not be trusted when secure boot is disabled */ |
215 | if (!arch_ima_get_secureboot()) |
216 | return 0; |
217 | |
218 | mokx = get_cert_list(name: L"MokListXRT" , guid: &mok_var, size: &mokxsize, status: &status); |
219 | if (!mokx) { |
220 | if (status == EFI_NOT_FOUND) |
221 | pr_debug("mokx variable wasn't found\n" ); |
222 | else |
223 | pr_info("Couldn't get mokx list\n" ); |
224 | } else { |
225 | rc = parse_efi_signature_list(source: "UEFI:MokListXRT" , |
226 | data: mokx, size: mokxsize, |
227 | get_handler_for_guid: get_handler_for_dbx); |
228 | if (rc) |
229 | pr_err("Couldn't parse mokx signatures %d\n" , rc); |
230 | kfree(objp: mokx); |
231 | } |
232 | |
233 | /* Load the MokListRT certs */ |
234 | rc = load_moklist_certs(); |
235 | |
236 | return rc; |
237 | } |
238 | late_initcall(load_uefi_certs); |
239 | |