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