xref: /qemu/hw/uefi/var-service-policy.c (revision f07a5674cf97b8473e5d06d7b1df9b51e97d553f)
1 /*
2  * SPDX-License-Identifier: GPL-2.0-or-later
3  *
4  * uefi vars device - VarCheckPolicyLibMmiHandler implementation
5  *
6  * variable policy specs:
7  * https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md
8  */
9 #include "qemu/osdep.h"
10 #include "system/dma.h"
11 #include "migration/vmstate.h"
12 
13 #include "hw/uefi/var-service.h"
14 #include "hw/uefi/var-service-api.h"
15 #include "hw/uefi/var-service-edk2.h"
16 
17 #include "trace/trace-hw_uefi.h"
18 
19 static void calc_policy(uefi_var_policy *pol);
20 
21 static int uefi_var_policy_post_load(void *opaque, int version_id)
22 {
23     uefi_var_policy *pol = opaque;
24 
25     calc_policy(pol);
26     return 0;
27 }
28 
29 const VMStateDescription vmstate_uefi_var_policy = {
30     .name = "uefi-var-policy",
31     .post_load = uefi_var_policy_post_load,
32     .fields = (VMStateField[]) {
33         VMSTATE_UINT32(entry_size, uefi_var_policy),
34         VMSTATE_VBUFFER_ALLOC_UINT32(entry, uefi_var_policy,
35                                      0, NULL, entry_size),
36         VMSTATE_END_OF_LIST()
37     },
38 };
39 
40 static void print_policy_entry(variable_policy_entry *pe)
41 {
42     uint16_t *name = (void *)pe + pe->offset_to_name;
43 
44     fprintf(stderr, "%s:\n", __func__);
45 
46     fprintf(stderr, "    name ´");
47     while (*name) {
48         fprintf(stderr, "%c", *name);
49         name++;
50     }
51     fprintf(stderr, "', version=%d.%d, size=%d\n",
52             pe->version >> 16, pe->version & 0xffff, pe->size);
53 
54     if (pe->min_size) {
55         fprintf(stderr, "    size min=%d\n", pe->min_size);
56     }
57     if (pe->max_size != UINT32_MAX) {
58         fprintf(stderr, "    size max=%u\n", pe->max_size);
59     }
60     if (pe->attributes_must_have) {
61         fprintf(stderr, "    attr must=0x%x\n", pe->attributes_must_have);
62     }
63     if (pe->attributes_cant_have) {
64         fprintf(stderr, "    attr cant=0x%x\n", pe->attributes_cant_have);
65     }
66     if (pe->lock_policy_type) {
67         fprintf(stderr, "    lock policy type %d\n", pe->lock_policy_type);
68     }
69 }
70 
71 static gboolean wildcard_str_equal(uefi_var_policy *pol,
72                                    uefi_variable *var)
73 {
74     return uefi_str_equal_ex(pol->name, pol->name_size,
75                              var->name, var->name_size,
76                              true);
77 }
78 
79 static uefi_var_policy *find_policy(uefi_vars_state *uv, QemuUUID guid,
80                                     uint16_t *name, uint64_t name_size)
81 {
82     uefi_var_policy *pol;
83 
84     QTAILQ_FOREACH(pol, &uv->var_policies, next) {
85         if (!qemu_uuid_is_equal(&pol->entry->namespace, &guid)) {
86             continue;
87         }
88         if (!uefi_str_equal(pol->name, pol->name_size,
89                             name, name_size)) {
90             continue;
91         }
92         return pol;
93     }
94     return NULL;
95 }
96 
97 static uefi_var_policy *wildcard_find_policy(uefi_vars_state *uv,
98                                              uefi_variable *var)
99 {
100     uefi_var_policy *pol;
101 
102     QTAILQ_FOREACH(pol, &uv->var_policies, next) {
103         if (!qemu_uuid_is_equal(&pol->entry->namespace, &var->guid)) {
104             continue;
105         }
106         if (!wildcard_str_equal(pol, var)) {
107             continue;
108         }
109         return pol;
110     }
111     return NULL;
112 }
113 
114 static void calc_policy(uefi_var_policy *pol)
115 {
116     variable_policy_entry *pe = pol->entry;
117     unsigned int i;
118 
119     pol->name = (void *)pol->entry + pe->offset_to_name;
120     pol->name_size = pe->size - pe->offset_to_name;
121 
122     for (i = 0; i < pol->name_size / 2; i++) {
123         if (pol->name[i] == '#') {
124             pol->hashmarks++;
125         }
126     }
127 }
128 
129 uefi_var_policy *uefi_vars_add_policy(uefi_vars_state *uv,
130                                       variable_policy_entry *pe)
131 {
132     uefi_var_policy *pol, *p;
133 
134     pol = g_new0(uefi_var_policy, 1);
135     pol->entry = g_malloc(pe->size);
136     memcpy(pol->entry, pe, pe->size);
137     pol->entry_size = pe->size;
138 
139     calc_policy(pol);
140 
141     /* keep list sorted by priority, add to tail of priority group */
142     QTAILQ_FOREACH(p, &uv->var_policies, next) {
143         if ((p->hashmarks > pol->hashmarks) ||
144             (!p->name_size && pol->name_size)) {
145             QTAILQ_INSERT_BEFORE(p, pol, next);
146             return pol;
147         }
148     }
149 
150     QTAILQ_INSERT_TAIL(&uv->var_policies, pol, next);
151     return pol;
152 }
153 
154 efi_status uefi_vars_policy_check(uefi_vars_state *uv,
155                                   uefi_variable *var,
156                                   gboolean is_newvar)
157 {
158     uefi_var_policy *pol;
159     variable_policy_entry *pe;
160     variable_lock_on_var_state *lvarstate;
161     uint16_t *lvarname;
162     size_t lvarnamesize;
163     uefi_variable *lvar;
164 
165     if (!uv->end_of_dxe) {
166         return EFI_SUCCESS;
167     }
168 
169     pol = wildcard_find_policy(uv, var);
170     if (!pol) {
171         return EFI_SUCCESS;
172     }
173     pe = pol->entry;
174 
175     uefi_trace_variable(__func__, var->guid, var->name, var->name_size);
176     print_policy_entry(pe);
177 
178     if ((var->attributes & pe->attributes_must_have) != pe->attributes_must_have) {
179         trace_uefi_vars_policy_deny("must-have-attr");
180         return EFI_INVALID_PARAMETER;
181     }
182     if ((var->attributes & pe->attributes_cant_have) != 0) {
183         trace_uefi_vars_policy_deny("cant-have-attr");
184         return EFI_INVALID_PARAMETER;
185     }
186 
187     if (var->data_size < pe->min_size) {
188         trace_uefi_vars_policy_deny("min-size");
189         return EFI_INVALID_PARAMETER;
190     }
191     if (var->data_size > pe->max_size) {
192         trace_uefi_vars_policy_deny("max-size");
193         return EFI_INVALID_PARAMETER;
194     }
195 
196     switch (pe->lock_policy_type) {
197     case VARIABLE_POLICY_TYPE_NO_LOCK:
198         break;
199 
200     case VARIABLE_POLICY_TYPE_LOCK_NOW:
201         trace_uefi_vars_policy_deny("lock-now");
202         return EFI_WRITE_PROTECTED;
203 
204     case VARIABLE_POLICY_TYPE_LOCK_ON_CREATE:
205         if (!is_newvar) {
206             trace_uefi_vars_policy_deny("lock-on-create");
207             return EFI_WRITE_PROTECTED;
208         }
209         break;
210 
211     case VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE:
212         lvarstate    = (void *)pol->entry + sizeof(*pe);
213         lvarname     = (void *)pol->entry + sizeof(*pe) + sizeof(*lvarstate);
214         lvarnamesize = pe->offset_to_name - sizeof(*pe) - sizeof(*lvarstate);
215 
216         uefi_trace_variable(__func__, lvarstate->namespace,
217                             lvarname, lvarnamesize);
218         lvar = uefi_vars_find_variable(uv, lvarstate->namespace,
219                                           lvarname, lvarnamesize);
220         if (lvar && lvar->data_size == 1) {
221             uint8_t *value = lvar->data;
222             if (lvarstate->value == *value) {
223                 return EFI_WRITE_PROTECTED;
224             }
225         }
226         break;
227     }
228 
229     return EFI_SUCCESS;
230 }
231 
232 void uefi_vars_policies_clear(uefi_vars_state *uv)
233 {
234     uefi_var_policy *pol;
235 
236     while (!QTAILQ_EMPTY(&uv->var_policies)) {
237         pol = QTAILQ_FIRST(&uv->var_policies);
238         QTAILQ_REMOVE(&uv->var_policies, pol, next);
239         g_free(pol->entry);
240         g_free(pol);
241     }
242 }
243 
244 static size_t uefi_vars_mm_policy_error(mm_header *mhdr,
245                                         mm_check_policy *mchk,
246                                         uint64_t status)
247 {
248     mchk->result = status;
249     return sizeof(*mchk);
250 }
251 
252 static uint32_t uefi_vars_mm_check_policy_is_enabled(uefi_vars_state *uv,
253                                                      mm_header       *mhdr,
254                                                      mm_check_policy *mchk,
255                                                      void            *func)
256 {
257     mm_check_policy_is_enabled *mpar = func;
258     size_t length;
259 
260     length = sizeof(*mchk) + sizeof(*mpar);
261     if (mhdr->length < length) {
262         return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE);
263     }
264 
265     mpar->state  = TRUE;
266     mchk->result = EFI_SUCCESS;
267     return sizeof(*mchk);
268 }
269 
270 static uint32_t uefi_vars_mm_check_policy_register(uefi_vars_state *uv,
271                                                    mm_header       *mhdr,
272                                                    mm_check_policy *mchk,
273                                                    void            *func)
274 {
275     variable_policy_entry *pe = func;
276     uefi_var_policy *pol;
277     uint64_t length;
278 
279     if (uadd64_overflow(sizeof(*mchk), pe->size, &length)) {
280         return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE);
281     }
282     if (mhdr->length < length) {
283         return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE);
284     }
285     if (pe->size < sizeof(*pe)) {
286         return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE);
287     }
288     if (pe->offset_to_name < sizeof(*pe)) {
289         return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE);
290     }
291 
292     if (pe->lock_policy_type == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE &&
293         pe->offset_to_name < sizeof(*pe) + sizeof(variable_lock_on_var_state)) {
294         return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE);
295     }
296 
297     /* check space for minimum string length */
298     if (pe->size < (size_t)pe->offset_to_name) {
299         return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE);
300     }
301 
302     if (!uefi_str_is_valid((void *)pe + pe->offset_to_name,
303                            pe->size - pe->offset_to_name,
304                            false)) {
305         return uefi_vars_mm_policy_error(mhdr, mchk, EFI_INVALID_PARAMETER);
306     }
307 
308     pol = find_policy(uv, pe->namespace,
309                       (void *)pe + pe->offset_to_name,
310                       pe->size - pe->offset_to_name);
311     if (pol) {
312         return uefi_vars_mm_policy_error(mhdr, mchk, EFI_ALREADY_STARTED);
313     }
314 
315     uefi_vars_add_policy(uv, pe);
316 
317     mchk->result = EFI_SUCCESS;
318     return sizeof(*mchk);
319 }
320 
321 uint32_t uefi_vars_mm_check_policy_proto(uefi_vars_state *uv)
322 {
323     static const char *fnames[] = {
324         "zero",
325         "disable",
326         "is-enabled",
327         "register",
328         "dump",
329         "lock",
330     };
331     const char      *fname;
332     mm_header       *mhdr = (mm_header *) uv->buffer;
333     mm_check_policy *mchk = (mm_check_policy *) (uv->buffer + sizeof(*mhdr));
334     void            *func = (uv->buffer + sizeof(*mhdr) + sizeof(*mchk));
335 
336     if (mhdr->length < sizeof(*mchk)) {
337         return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE;
338     }
339 
340     fname = mchk->command < ARRAY_SIZE(fnames)
341         ? fnames[mchk->command]
342         : "unknown";
343     trace_uefi_vars_policy_cmd(fname);
344 
345     switch (mchk->command) {
346     case VAR_CHECK_POLICY_COMMAND_DISABLE:
347         mchk->result = EFI_UNSUPPORTED;
348         break;
349     case VAR_CHECK_POLICY_COMMAND_IS_ENABLED:
350         uefi_vars_mm_check_policy_is_enabled(uv, mhdr, mchk, func);
351         break;
352     case VAR_CHECK_POLICY_COMMAND_REGISTER:
353         if (uv->policy_locked) {
354             mchk->result = EFI_WRITE_PROTECTED;
355         } else {
356             uefi_vars_mm_check_policy_register(uv, mhdr, mchk, func);
357         }
358         break;
359     case VAR_CHECK_POLICY_COMMAND_LOCK:
360         uv->policy_locked = true;
361         mchk->result = EFI_SUCCESS;
362         break;
363     default:
364         mchk->result = EFI_UNSUPPORTED;
365         break;
366     }
367 
368     uefi_trace_status(__func__, mchk->result);
369     return UEFI_VARS_STS_SUCCESS;
370 }
371