diff options
author | Josh Boyer <jwboyer@fedoraproject.org> | 2018-12-12 15:07:56 -0500 |
---|---|---|
committer | Mimi Zohar <zohar@linux.ibm.com> | 2018-12-12 22:04:33 -0500 |
commit | 15ea0e1e3e185040bed6119f815096f2e4326242 (patch) | |
tree | cc0ecb830489dc77ffcec87b2500ac9aa540b31f /security | |
parent | 0bc9ae395b3f3b6557f0c5f0a0b0cd2fd5c00a04 (diff) |
efi: Import certificates from UEFI Secure Boot
Secure Boot stores a list of allowed certificates in the 'db' variable.
This patch imports those certificates into the platform keyring. The shim
UEFI bootloader has a similar certificate list stored in the 'MokListRT'
variable. We import those as well.
Secure Boot also maintains a list of disallowed certificates in the 'dbx'
variable. We load those certificates into the system blacklist keyring
and forbid any kernel signed with those from loading.
[zohar@linux.ibm.com: dropped Josh's original patch description]
Signed-off-by: Josh Boyer <jwboyer@fedoraproject.org>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
Acked-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
Diffstat (limited to 'security')
-rw-r--r-- | security/integrity/Makefile | 5 | ||||
-rw-r--r-- | security/integrity/platform_certs/load_uefi.c | 169 |
2 files changed, 173 insertions, 1 deletions
diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 6ee9058866cd..86df9aba8c0f 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile | |||
@@ -10,7 +10,10 @@ integrity-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o | |||
10 | integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o | 10 | integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o |
11 | integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o | 11 | integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o |
12 | integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyring.o \ | 12 | integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyring.o \ |
13 | platform_certs/efi_parser.o | 13 | platform_certs/efi_parser.o \ |
14 | platform_certs/load_uefi.o | ||
15 | obj-$(CONFIG_LOAD_UEFI_KEYS) += platform_certs/load_uefi.o | ||
16 | $(obj)/load_uefi.o: KBUILD_CFLAGS += -fshort-wchar | ||
14 | 17 | ||
15 | subdir-$(CONFIG_IMA) += ima | 18 | subdir-$(CONFIG_IMA) += ima |
16 | obj-$(CONFIG_IMA) += ima/ | 19 | obj-$(CONFIG_IMA) += ima/ |
diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c new file mode 100644 index 000000000000..8ceafa58d98c --- /dev/null +++ b/security/integrity/platform_certs/load_uefi.c | |||
@@ -0,0 +1,169 @@ | |||
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/err.h> | ||
7 | #include <linux/efi.h> | ||
8 | #include <linux/slab.h> | ||
9 | #include <keys/asymmetric-type.h> | ||
10 | #include <keys/system_keyring.h> | ||
11 | #include "../integrity.h" | ||
12 | |||
13 | static efi_guid_t efi_cert_x509_guid __initdata = EFI_CERT_X509_GUID; | ||
14 | static efi_guid_t efi_cert_x509_sha256_guid __initdata = | ||
15 | EFI_CERT_X509_SHA256_GUID; | ||
16 | static efi_guid_t efi_cert_sha256_guid __initdata = EFI_CERT_SHA256_GUID; | ||
17 | |||
18 | /* | ||
19 | * Get a certificate list blob from the named EFI variable. | ||
20 | */ | ||
21 | static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, | ||
22 | unsigned long *size) | ||
23 | { | ||
24 | efi_status_t status; | ||
25 | unsigned long lsize = 4; | ||
26 | unsigned long tmpdb[4]; | ||
27 | void *db; | ||
28 | |||
29 | status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb); | ||
30 | if (status != EFI_BUFFER_TOO_SMALL) { | ||
31 | pr_err("Couldn't get size: 0x%lx\n", status); | ||
32 | return NULL; | ||
33 | } | ||
34 | |||
35 | db = kmalloc(lsize, GFP_KERNEL); | ||
36 | if (!db) | ||
37 | return NULL; | ||
38 | |||
39 | status = efi.get_variable(name, guid, NULL, &lsize, db); | ||
40 | if (status != EFI_SUCCESS) { | ||
41 | kfree(db); | ||
42 | pr_err("Error reading db var: 0x%lx\n", status); | ||
43 | return NULL; | ||
44 | } | ||
45 | |||
46 | *size = lsize; | ||
47 | return db; | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * Blacklist a hash. | ||
52 | */ | ||
53 | static __init void uefi_blacklist_hash(const char *source, const void *data, | ||
54 | size_t len, const char *type, | ||
55 | size_t type_len) | ||
56 | { | ||
57 | char *hash, *p; | ||
58 | |||
59 | hash = kmalloc(type_len + len * 2 + 1, GFP_KERNEL); | ||
60 | if (!hash) | ||
61 | return; | ||
62 | p = memcpy(hash, type, type_len); | ||
63 | p += type_len; | ||
64 | bin2hex(p, data, len); | ||
65 | p += len * 2; | ||
66 | *p = 0; | ||
67 | |||
68 | mark_hash_blacklisted(hash); | ||
69 | kfree(hash); | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * Blacklist an X509 TBS hash. | ||
74 | */ | ||
75 | static __init void uefi_blacklist_x509_tbs(const char *source, | ||
76 | const void *data, size_t len) | ||
77 | { | ||
78 | uefi_blacklist_hash(source, data, len, "tbs:", 4); | ||
79 | } | ||
80 | |||
81 | /* | ||
82 | * Blacklist the hash of an executable. | ||
83 | */ | ||
84 | static __init void uefi_blacklist_binary(const char *source, | ||
85 | const void *data, size_t len) | ||
86 | { | ||
87 | uefi_blacklist_hash(source, data, len, "bin:", 4); | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * Return the appropriate handler for particular signature list types found in | ||
92 | * the UEFI db and MokListRT tables. | ||
93 | */ | ||
94 | static __init efi_element_handler_t get_handler_for_db(const efi_guid_t * | ||
95 | sig_type) | ||
96 | { | ||
97 | if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0) | ||
98 | return add_to_platform_keyring; | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * Return the appropriate handler for particular signature list types found in | ||
104 | * the UEFI dbx and MokListXRT tables. | ||
105 | */ | ||
106 | static __init efi_element_handler_t get_handler_for_dbx(const efi_guid_t * | ||
107 | sig_type) | ||
108 | { | ||
109 | if (efi_guidcmp(*sig_type, efi_cert_x509_sha256_guid) == 0) | ||
110 | return uefi_blacklist_x509_tbs; | ||
111 | if (efi_guidcmp(*sig_type, efi_cert_sha256_guid) == 0) | ||
112 | return uefi_blacklist_binary; | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | /* | ||
117 | * Load the certs contained in the UEFI databases | ||
118 | */ | ||
119 | static int __init load_uefi_certs(void) | ||
120 | { | ||
121 | efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; | ||
122 | efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; | ||
123 | void *db = NULL, *dbx = NULL, *mok = NULL; | ||
124 | unsigned long dbsize = 0, dbxsize = 0, moksize = 0; | ||
125 | int rc = 0; | ||
126 | |||
127 | if (!efi.get_variable) | ||
128 | return false; | ||
129 | |||
130 | /* Get db, MokListRT, and dbx. They might not exist, so it isn't | ||
131 | * an error if we can't get them. | ||
132 | */ | ||
133 | db = get_cert_list(L"db", &secure_var, &dbsize); | ||
134 | if (!db) { | ||
135 | pr_err("Couldn't get UEFI db list\n"); | ||
136 | } else { | ||
137 | rc = parse_efi_signature_list("UEFI:db", | ||
138 | db, dbsize, get_handler_for_db); | ||
139 | if (rc) | ||
140 | pr_err("Couldn't parse db signatures: %d\n", rc); | ||
141 | kfree(db); | ||
142 | } | ||
143 | |||
144 | mok = get_cert_list(L"MokListRT", &mok_var, &moksize); | ||
145 | if (!mok) { | ||
146 | pr_info("Couldn't get UEFI MokListRT\n"); | ||
147 | } else { | ||
148 | rc = parse_efi_signature_list("UEFI:MokListRT", | ||
149 | mok, moksize, get_handler_for_db); | ||
150 | if (rc) | ||
151 | pr_err("Couldn't parse MokListRT signatures: %d\n", rc); | ||
152 | kfree(mok); | ||
153 | } | ||
154 | |||
155 | dbx = get_cert_list(L"dbx", &secure_var, &dbxsize); | ||
156 | if (!dbx) { | ||
157 | pr_info("Couldn't get UEFI dbx list\n"); | ||
158 | } else { | ||
159 | rc = parse_efi_signature_list("UEFI:dbx", | ||
160 | dbx, dbxsize, | ||
161 | get_handler_for_dbx); | ||
162 | if (rc) | ||
163 | pr_err("Couldn't parse dbx signatures: %d\n", rc); | ||
164 | kfree(dbx); | ||
165 | } | ||
166 | |||
167 | return rc; | ||
168 | } | ||
169 | late_initcall(load_uefi_certs); | ||