summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md
blob: 6f176060b9a5792f3a1cb71b90abb4a78e4dca1b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
---
title:      UEFI Variable Policy Whitepaper
version:    1.0
copyright:  Copyright (c) Microsoft Corporation.
---

# UEFI Variable Policy

## Summary

UEFI Variable Policy spec aims to describe the DXE protocol interface
which allows enforcing certain rules on certain UEFI variables. The
protocol allows communication with the Variable Policy Engine which
performs the policy enforcement.

The Variable Policy is comprised of a set of policy entries which
describe, per UEFI variable (identified by namespace GUID and variable
name) the following rules:

-   Required variable attributes
-   Prohibited variable attributes
-   Minimum variable size
-   Maximum variable size
-   Locking:
    -   Locking "immediately"
    -   Locking on creation
    -   Locking based on a state of another variable

The spec assumes that the Variable Policy Engine runs in a trusted
enclave, potentially off the main CPU that runs UEFI. For that reason,
it is assumed that the Variable Policy Engine has no concept of UEFI
events, and that the communication from the DXE driver to the trusted
enclave is proprietary.

At power-on, the Variable Policy Engine is:

-   Enabled -- present policy entries are evaluated on variable access
    calls.
-   Unlocked -- new policy entries can be registered.

Policy is expected to be clear on power-on. Policy is volatile and not
preserved across system reset.

## DXE Protocol

```h
typedef struct {
  UINT64                        Revision;
  DISABLE_VARIABLE_POLICY       DisableVariablePolicy;
  IS_VARIABLE_POLICY_ENABLED    IsVariablePolicyEnabled;
  REGISTER_VARIABLE_POLICY      RegisterVariablePolicy;
  DUMP_VARIABLE_POLICY          DumpVariablePolicy;
  LOCK_VARIABLE_POLICY          LockVariablePolicy;
} _VARIABLE_POLICY_PROTOCOL;

typedef _VARIABLE_POLICY_PROTOCOL VARIABLE_POLICY_PROTOCOL;

extern EFI_GUID gVariablePolicyProtocolGuid;
```

```text
## Include/Protocol/VariablePolicy.h
  gVariablePolicyProtocolGuid = { 0x81D1675C, 0x86F6, 0x48DF, { 0xBD, 0x95, 0x9A, 0x6E, 0x4F, 0x09, 0x25, 0xC3 } }
```

### DisableVariablePolicy

Function prototype:

```c
EFI_STATUS
EFIAPI
DisableVariablePolicy (
  VOID
  );
```

`DisableVariablePolicy` call disables the Variable Policy Engine, so
that the present policy entries are no longer taken into account on
variable access calls. This call effectively turns off the variable
policy verification for this boot. This also disables UEFI
Authenticated Variable protections including Secure Boot.
`DisableVariablePolicy` can only be called once during boot. If called
more than once, it will return `EFI_ALREADY_STARTED`. Note, this process
is irreversible until the next system reset -- there is no
"EnablePolicy" protocol function.

_IMPORTANT NOTE:_ It is strongly recommended that VariablePolicy *NEVER*
be disabled in "normal, production boot conditions". It is expected to always
be enforced. The most likely reasons to disable are for Manufacturing and
Refurbishing scenarios. If in doubt, leave the `gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable`
PCD set to `FALSE` and VariablePolicy will always be enabled.

### IsVariablePolicyEnabled

Function prototype:

```c
EFI_STATUS
EFIAPI
IsVariablePolicyEnabled (
  OUT BOOLEAN   *State
  );
```

`IsVariablePolicyEnabled` accepts a pointer to a Boolean in which it
will store `TRUE` if Variable Policy Engine is enabled, or `FALSE` if
Variable Policy Engine is disabled. The function returns `EFI_SUCCESS`.

### RegisterVariablePolicy

Function prototype:

```c
EFI_STATUS
EFIAPI
RegisterVariablePolicy (
  IN CONST VARIABLE_POLICY_ENTRY  *PolicyEntry
  );
```

`RegisterVariablePolicy` call accepts a pointer to a policy entry
structure and returns the status of policy registration. If the
Variable Policy Engine is not locked and the policy structures are
valid, the function will return `EFI_SUCCESS`. If the Variable Policy
Engine is locked, `RegisterVariablePolicy` call will return
`EFI_WRITE_PROTECTED` and will not register the policy entry. Bulk
registration is not supported at this time due to the requirements
around error handling on each policy registration.

Upon successful registration of a policy entry, Variable Policy Engine
will then evaluate this entry on subsequent variable access calls (as
long as Variable Policy Engine hasn't been disabled).

### DumpVariablePolicy

Function prototype:

```c
EFI_STATUS
EFIAPI
DumpVariablePolicy (
  OUT     UINT8     *Policy,
  IN OUT  UINT32    *Size
  );
```

`DumpVariablePolicy` call accepts a pointer to a buffer and a pointer to
the size of the buffer as parameters and returns the status of placing
the policy into the buffer. On first call to `DumpVariablePolicy` one
should pass `NULL` as the buffer and a pointer to 0 as the `Size` variable
and `DumpVariablePolicy` will return `EFI_BUFFER_TOO_SMALL` and will
populate the `Size` parameter with the size of the needed buffer to
store the policy. This way, the caller can allocate the buffer of
correct size and call `DumpVariablePolicy` again. The function will
populate the buffer with policy and return `EFI_SUCCESS`.

### LockVariablePolicy

Function prototype:

```c
EFI_STATUS
EFIAPI
LockVariablePolicy (
  VOID
  );
```

`LockVariablePolicy` locks the Variable Policy Engine, i.e. prevents any
new policy entries from getting registered in this boot
(`RegisterVariablePolicy` calls will fail with `EFI_WRITE_PROTECTED`
status code returned).

## Policy Structure

The structure below is meant for the DXE protocol calling interface,
when communicating to the Variable Policy Engine, thus the pragma pack
directive. How these policies are stored in memory is up to the
implementation.

```c
#pragma pack(1)
typedef struct {
  UINT32    Version;
  UINT16    Size;
  UINT16    OffsetToName;
  EFI_GUID  Namespace;
  UINT32    MinSize;
  UINT32    MaxSize;
  UINT32    AttributesMustHave;
  UINT32    AttributesCantHave;
  UINT8     LockPolicyType;
  UINT8     Reserved[3];
  // UINT8  LockPolicy[]; // Variable Length Field
  // CHAR16 Name[];       // Variable Length Field
} VARIABLE_POLICY_ENTRY;
```

The struct `VARIABLE_POLICY_ENTRY` above describes the layout for a policy
entry. The first element, `Size`, is the size of the policy entry, then
followed by `OffsetToName` -- the number of bytes from the beginning of
the struct to the name of the UEFI variable targeted by the policy
entry. The name can contain wildcards to match more than one variable,
more on this in the Wildcards section. The rest of the struct elements
are self-explanatory.

```cpp
#define VARIABLE_POLICY_TYPE_NO_LOCK            0
#define VARIABLE_POLICY_TYPE_LOCK_NOW           1
#define VARIABLE_POLICY_TYPE_LOCK_ON_CREATE     2
#define VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE  3
```

`LockPolicyType` can have the following values:

-   `VARIABLE_POLICY_TYPE_NO_LOCK` -- means that no variable locking is performed. However,
    the attribute and size constraints are still enforced. LockPolicy
    field is size 0.
-   `VARIABLE_POLICY_TYPE_LOCK_NOW` -- means that the variable starts being locked
    immediately after policy entry registration. If the variable doesn't
    exist at this point, being LockedNow means it cannot be created on
    this boot. LockPolicy field is size 0.
-   `VARIABLE_POLICY_TYPE_LOCK_ON_CREATE` -- means that the variable starts being locked
    after it is created. This allows for variable creation and
    protection after LockVariablePolicy() function has been called. The
    LockPolicy field is size 0.
-   `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE` -- means that the Variable Policy Engine will
    examine the state/contents of another variable to determine if the
    variable referenced in the policy entry is locked.

```c
typedef struct {
  EFI_GUID  Namespace;
  UINT8     Value;
  UINT8     Reserved;
  // CHAR16 Name[];   // Variable Length Field
} VARIABLE_LOCK_ON_VAR_STATE_POLICY;
```

If `LockPolicyType` is `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE`, then the final element in the
policy entry struct is of type `VARIABLE_LOCK_ON_VAR_STATE_POLICY`, which
lists the namespace GUID, name (no wildcards here), and value of the
variable which state determines the locking of the variable referenced
in the policy entry. The "locking" variable must be 1 byte in terms of
payload size. If the Referenced variable contents match the Value of the
`VARIABLE_LOCK_ON_VAR_STATE_POLICY` structure, the lock will be considered
active and the target variable will be locked. If the Reference variable
does not exist (ie. returns `EFI_NOT_FOUND`), this policy will be
considered inactive.

## Variable Name Wildcards

Two types of wildcards can be used in the UEFI variable name field in a
policy entry:

1.  If the Name is a zero-length array (easily checked by comparing
    fields `Size` and `OffsetToName` -- if they're the same, then the
    `Name` is zero-length), then all variables in the namespace specified
    by the provided GUID are targeted by the policy entry.
2.  Character "#" in the `Name` corresponds to one numeric character
    (0-9, A-F, a-f). For example, string "Boot####" in the `Name`
    field of the policy entry will make it so that the policy entry will
    target variables named "Boot0001", "Boot0002", etc.

Given the above two types of wildcards, one variable can be targeted by
more than one policy entry, thus there is a need to establish the
precedence rule: a more specific match is applied. When a variable
access operation is performed, Variable Policy Engine should first check
the variable being accessed against the policy entries without
wildcards, then with 1 wildcard, then with 2 wildcards, etc., followed
in the end by policy entries that match the whole namespace. One can
still imagine a situation where two policy entries with the same number
of wildcards match the same variable -- for example, policy entries with
Names "Boot00##" and "Boot##01" will both match variable "Boot0001".
Such situation can (and should) be avoided by designing mutually
exclusive Name strings with wildcards, however, if it occurs, then the
policy entry that was registered first will be used. After the most
specific match is selected, all other policies are ignored.

## Available Testing

This functionality is current supported by two kinds of tests: there is a host-based
unit test for the core business logic (this test accompanies the `VariablePolicyLib`
implementation that lives in `MdeModulePkg/Library`) and there is a functional test
for the protocol and its interfaces (this test lives in the `MdeModulePkg/Test/ShellTest`
directory).

### Host-Based Unit Test

There is a test that can be run as part of the Host-Based Unit Testing
infrastructure provided by EDK2 PyTools (documented elsewhere). It will test
all internal guarantees and is where you will find test cases for most of the
policy matching and security of the Variable Policy Engine.

### Shell-Based Functional Test

This test -- [Variable Policy Functional Unit Test](https://github.com/microsoft/mu_plus/tree/release/202005/UefiTestingPkg/FunctionalSystemTests/VarPolicyUnitTestApp) -- can be built as a
UEFI Shell application and run to validate that the Variable Policy Engine
is correctly installed and enforcing policies on the target system.

NOTE: This test _must_ be run prior to calling `DisableVariablePolicy` for all
test cases to pass. For this reason, it is recommended to run this on a test-built
FW for complete results, and then again on a production-built FW for release
results.

## Use Cases

The below examples are hypothetical scenarios based on real-world requirements
that demonstrate how Variable Policies could be constructed to solve various
problems.

### UEFI Setup Variables (Example 1)

Variables containing values of the setup options exposed via UEFI
menu (setup variables). These would be locked based on a state of
another variable, "ReadyToBoot", which would be set to 1 at the
ReadyToBoot event. Thus, the policy for the setup variables would be
of type `LockOnVarState`, with the "ReadyToBoot" listed as the name of
the variable, appropriate GUID listed as the namespace, and 1 as
value. Entry into the trusted UEFI menu app doesn't signal
ReadyToBoot, but booting to any device does, and the setup variables
are write-protected. The "ReadyToBoot" variable would need to be
locked-on-create. *(THIS IS ESSENTIALLY LOCK ON EVENT, BUT SINCE THE
POLICY ENGINE IS NOT IN THE UEFI ENVIRONMENT VARIABLES ARE USED)*

For example, "AllowPXEBoot" variable locked by "ReadyToBoot" variable.

(NOTE: In the below example, the emphasized fields ('Namespace', 'Value', and 'Name')
are members of the `VARIABLE_LOCK_ON_VAR_STATE_POLICY` structure.)

Size                  | ...
----                  | ---
OffsetToName          | ...
NameSpace             | ...
MinSize               | ...
MaxSize               | ...
AttributesMustHave    | ...
AttributesCantHave    | ...
LockPolicyType        | `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE`
_Namespace_           | ...
_Value_               | 1
_Name_                | "ReadyToBoot"
//Name                | "AllowPXEBoot"

### Manufacturing VPD (Example 2)

Manufacturing Variable Provisioning Data (VPD) is stored in
variables and is created while in Manufacturing (MFG) Mode. In MFG
Mode Variable Policy Engine is disabled, thus these VPD variables
can be created. These variables are locked with lock policy type
`LockNow`, so that these variables can't be tampered with in Customer
Mode. To overwrite or clear VPD, the device would need to MFG mode,
which is standard practice for refurbishing/remanufacturing
scenarios.

Example: "DisplayPanelCalibration" variable...

Size                  | ...
----                  | ---
OffsetToName          | ...
NameSpace             | ...
MinSize               | ...
MaxSize               | ...
AttributesMustHave    | ...
AttributesCantHave    | ...
LockPolicyType        | `VARIABLE_POLICY_TYPE_LOCK_NOW`
// Name               | "DisplayPanelCalibration"

### 3rd Party Calibration Data (Example 3)

Bluetooth pre-pairing variables are locked-on-create because these
get created by an OS application when Variable Policy is in effect.

Example: "KeyboardBTPairing" variable

Size                  | ...
----                  | ---
OffsetToName          | ...
NameSpace             | ...
MinSize               | ...
MaxSize               | ...
AttributesMustHave    | ...
AttributesCantHave    | ...
LockPolicyType        | `VARIABLE_POLICY_TYPE_LOCK_ON_CREATE`
// Name               | "KeyboardBTPairing"

### Software-based Variable Policy (Example 4)

Example: "Boot####" variables (a name string with wildcards that
will match variables "Boot0000" to "BootFFFF") locked by "LockBootOrder"
variable.

Size                  | ...
----                  | ---
OffsetToName          | ...
NameSpace             | ...
MinSize               | ...
MaxSize               | ...
AttributesMustHave    | ...
AttributesCantHave    | ...
LockPolicyType        | `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE`
_Namespace_           | ...
_Value_               | 1
_Name_                | "LockBootOrder"
//Name                | "Boot####"