eficonfig: add UEFI Secure Boot Key enrollment interface
[platform/kernel/u-boot.git] / cmd / eficonfig_sbkey.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  Menu-driven UEFI Secure Boot Key Maintenance
4  *
5  *  Copyright (c) 2022 Masahisa Kojima, Linaro Limited
6  */
7
8 #include <ansi.h>
9 #include <common.h>
10 #include <charset.h>
11 #include <hexdump.h>
12 #include <log.h>
13 #include <malloc.h>
14 #include <menu.h>
15 #include <efi_loader.h>
16 #include <efi_config.h>
17 #include <efi_variable.h>
18 #include <crypto/pkcs7_parser.h>
19
20 enum efi_sbkey_signature_type {
21         SIG_TYPE_X509 = 0,
22         SIG_TYPE_HASH,
23         SIG_TYPE_CRL,
24         SIG_TYPE_RSA2048,
25 };
26
27 struct eficonfig_sigtype_to_str {
28         efi_guid_t sig_type;
29         char *str;
30         enum efi_sbkey_signature_type type;
31 };
32
33 static const struct eficonfig_sigtype_to_str sigtype_to_str[] = {
34         {EFI_CERT_X509_GUID,            "X509",                 SIG_TYPE_X509},
35         {EFI_CERT_SHA256_GUID,          "SHA256",               SIG_TYPE_HASH},
36         {EFI_CERT_X509_SHA256_GUID,     "X509_SHA256 CRL",      SIG_TYPE_CRL},
37         {EFI_CERT_X509_SHA384_GUID,     "X509_SHA384 CRL",      SIG_TYPE_CRL},
38         {EFI_CERT_X509_SHA512_GUID,     "X509_SHA512 CRL",      SIG_TYPE_CRL},
39         /* U-Boot does not support the following signature types */
40 /*      {EFI_CERT_RSA2048_GUID,         "RSA2048",              SIG_TYPE_RSA2048}, */
41 /*      {EFI_CERT_RSA2048_SHA256_GUID,  "RSA2048_SHA256",       SIG_TYPE_RSA2048}, */
42 /*      {EFI_CERT_SHA1_GUID,            "SHA1",                 SIG_TYPE_HASH}, */
43 /*      {EFI_CERT_RSA2048_SHA_GUID,     "RSA2048_SHA",          SIG_TYPE_RSA2048 }, */
44 /*      {EFI_CERT_SHA224_GUID,          "SHA224",               SIG_TYPE_HASH}, */
45 /*      {EFI_CERT_SHA384_GUID,          "SHA384",               SIG_TYPE_HASH}, */
46 /*      {EFI_CERT_SHA512_GUID,          "SHA512",               SIG_TYPE_HASH}, */
47 };
48
49 /**
50  * file_have_auth_header() - check file has EFI_VARIABLE_AUTHENTICATION_2 header
51  * @buf:        pointer to file
52  * @size:       file size
53  * Return:      true if file has auth header, false otherwise
54  */
55 static bool file_have_auth_header(void *buf, efi_uintn_t size)
56 {
57         struct efi_variable_authentication_2 *auth = buf;
58
59         if (auth->auth_info.hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID)
60                 return false;
61
62         if (guidcmp(&auth->auth_info.cert_type, &efi_guid_cert_type_pkcs7))
63                 return false;
64
65         return true;
66 }
67
68 /**
69  * eficonfig_process_enroll_key() - enroll key into signature database
70  *
71  * @data:       pointer to the data for each entry
72  * Return:      status code
73  */
74 static efi_status_t eficonfig_process_enroll_key(void *data)
75 {
76         u32 attr;
77         char *buf = NULL;
78         efi_uintn_t size;
79         efi_status_t ret;
80         struct efi_file_handle *f = NULL;
81         struct efi_device_path *full_dp = NULL;
82         struct eficonfig_select_file_info file_info;
83
84         file_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
85         if (!file_info.current_path) {
86                 ret = EFI_OUT_OF_RESOURCES;
87                 goto out;
88         }
89
90         ret = eficonfig_process_select_file(&file_info);
91         if (ret != EFI_SUCCESS)
92                 goto out;
93
94         full_dp = eficonfig_create_device_path(file_info.dp_volume, file_info.current_path);
95         if (!full_dp) {
96                 ret = EFI_OUT_OF_RESOURCES;
97                 goto out;
98         }
99         f = efi_file_from_path(full_dp);
100         if (!f) {
101                 ret = EFI_NOT_FOUND;
102                 goto out;
103         }
104
105         size = 0;
106         ret = EFI_CALL(f->getinfo(f, &efi_file_info_guid, &size, NULL));
107         if (ret != EFI_BUFFER_TOO_SMALL)
108                 goto out;
109
110         buf = malloc(size);
111         if (!buf) {
112                 ret = EFI_OUT_OF_RESOURCES;
113                 goto out;
114         }
115         ret = EFI_CALL(f->getinfo(f, &efi_file_info_guid, &size, buf));
116         if (ret != EFI_SUCCESS)
117                 goto out;
118
119         size = ((struct efi_file_info *)buf)->file_size;
120         free(buf);
121
122         if (!size) {
123                 eficonfig_print_msg("ERROR! File is empty.");
124                 ret = EFI_INVALID_PARAMETER;
125                 goto out;
126         }
127
128         buf = malloc(size);
129         if (!buf) {
130                 ret = EFI_OUT_OF_RESOURCES;
131                 goto out;
132         }
133
134         ret = EFI_CALL(f->read(f, &size, buf));
135         if (ret != EFI_SUCCESS) {
136                 eficonfig_print_msg("ERROR! Failed to read file.");
137                 goto out;
138         }
139         if (!file_have_auth_header(buf, size)) {
140                 eficonfig_print_msg("ERROR! Invalid file format. Only .auth variables is allowed.");
141                 ret = EFI_INVALID_PARAMETER;
142                 goto out;
143         }
144
145         attr = EFI_VARIABLE_NON_VOLATILE |
146                EFI_VARIABLE_BOOTSERVICE_ACCESS |
147                EFI_VARIABLE_RUNTIME_ACCESS |
148                EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
149
150         /* PK can enroll only one certificate */
151         if (u16_strcmp(data, u"PK")) {
152                 efi_uintn_t db_size = 0;
153
154                 /* check the variable exists. If exists, add APPEND_WRITE attribute */
155                 ret = efi_get_variable_int(data, efi_auth_var_get_guid(data), NULL,
156                                            &db_size, NULL,  NULL);
157                 if (ret == EFI_BUFFER_TOO_SMALL)
158                         attr |= EFI_VARIABLE_APPEND_WRITE;
159         }
160
161         ret = efi_set_variable_int((u16 *)data, efi_auth_var_get_guid((u16 *)data),
162                                    attr, size, buf, false);
163         if (ret != EFI_SUCCESS)
164                 eficonfig_print_msg("ERROR! Failed to update signature database");
165
166 out:
167         free(file_info.current_path);
168         free(buf);
169         efi_free_pool(full_dp);
170         if (f)
171                 EFI_CALL(f->close(f));
172
173         /* return to the parent menu */
174         ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
175
176         return ret;
177 }
178
179 static struct eficonfig_item key_config_menu_items[] = {
180         {"Enroll New Key", eficonfig_process_enroll_key},
181         {"Quit", eficonfig_process_quit},
182 };
183
184 /**
185  * eficonfig_process_set_secure_boot_key() - display the key configuration menu
186  *
187  * @data:       pointer to the data for each entry
188  * Return:      status code
189  */
190 static efi_status_t eficonfig_process_set_secure_boot_key(void *data)
191 {
192         u32 i;
193         efi_status_t ret;
194         char header_str[32];
195         struct efimenu *efi_menu;
196
197         for (i = 0; i < ARRAY_SIZE(key_config_menu_items); i++)
198                 key_config_menu_items[i].data = data;
199
200         snprintf(header_str, sizeof(header_str), "  ** Configure %ls **", (u16 *)data);
201
202         while (1) {
203                 efi_menu = eficonfig_create_fixed_menu(key_config_menu_items,
204                                                        ARRAY_SIZE(key_config_menu_items));
205
206                 ret = eficonfig_process_common(efi_menu, header_str);
207                 eficonfig_destroy(efi_menu);
208
209                 if (ret == EFI_ABORTED)
210                         break;
211         }
212
213         /* return to the parent menu */
214         ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
215
216         return ret;
217 }
218
219 static const struct eficonfig_item secure_boot_menu_items[] = {
220         {"PK", eficonfig_process_set_secure_boot_key, u"PK"},
221         {"KEK", eficonfig_process_set_secure_boot_key, u"KEK"},
222         {"db", eficonfig_process_set_secure_boot_key, u"db"},
223         {"dbx", eficonfig_process_set_secure_boot_key, u"dbx"},
224         {"Quit", eficonfig_process_quit},
225 };
226
227 /**
228  * eficonfig_process_secure_boot_config() - display the key list menu
229  *
230  * @data:       pointer to the data for each entry
231  * Return:      status code
232  */
233 efi_status_t eficonfig_process_secure_boot_config(void *data)
234 {
235         efi_status_t ret;
236         struct efimenu *efi_menu;
237
238         while (1) {
239                 char header_str[64];
240
241                 snprintf(header_str, sizeof(header_str),
242                          "  ** UEFI Secure Boot Key Configuration (SecureBoot : %s) **",
243                          (efi_secure_boot_enabled() ? "ON" : "OFF"));
244
245                 efi_menu = eficonfig_create_fixed_menu(secure_boot_menu_items,
246                                                        ARRAY_SIZE(secure_boot_menu_items));
247                 if (!efi_menu) {
248                         ret = EFI_OUT_OF_RESOURCES;
249                         break;
250                 }
251
252                 ret = eficonfig_process_common(efi_menu, header_str);
253                 eficonfig_destroy(efi_menu);
254
255                 if (ret == EFI_ABORTED)
256                         break;
257         }
258
259         /* return to the parent menu */
260         ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
261
262         return ret;
263 }