summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Library/VariablePolicyLib
diff options
context:
space:
mode:
Diffstat (limited to 'MdeModulePkg/Library/VariablePolicyLib')
-rw-r--r--MdeModulePkg/Library/VariablePolicyLib/ReadMe.md812
-rw-r--r--MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitNull.c88
-rw-r--r--MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitRuntimeDxe.c168
-rw-r--r--MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c2334
-rw-r--r--MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf98
-rw-r--r--MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni24
-rw-r--r--MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf102
7 files changed, 1813 insertions, 1813 deletions
diff --git a/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md b/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md
index c2f9850a12..6f176060b9 100644
--- a/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md
+++ b/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md
@@ -1,406 +1,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####"
+---
+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####"
diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitNull.c b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitNull.c
index bb701f14c6..8904f5253c 100644
--- a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitNull.c
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitNull.c
@@ -1,44 +1,44 @@
-/** @file -- VariablePolicyExtraInitNull.c
-This file contains extra init and deinit routines that don't do anything
-extra.
-
-Copyright (c) Microsoft Corporation.
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include <Library/UefiRuntimeServicesTableLib.h>
-
-/**
- An extra init hook that enables the RuntimeDxe library instance to
- register VirtualAddress change callbacks. Among other things.
-
- @retval EFI_SUCCESS Everything is good. Continue with init.
- @retval Others Uh... don't continue.
-
-**/
-EFI_STATUS
-VariablePolicyExtraInit (
- VOID
- )
-{
- // NULL implementation.
- return EFI_SUCCESS;
-}
-
-/**
- An extra deinit hook that enables the RuntimeDxe library instance to
- register VirtualAddress change callbacks. Among other things.
-
- @retval EFI_SUCCESS Everything is good. Continue with deinit.
- @retval Others Uh... don't continue.
-
-**/
-EFI_STATUS
-VariablePolicyExtraDeinit (
- VOID
- )
-{
- // NULL implementation.
- return EFI_SUCCESS;
-}
+/** @file -- VariablePolicyExtraInitNull.c
+This file contains extra init and deinit routines that don't do anything
+extra.
+
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+/**
+ An extra init hook that enables the RuntimeDxe library instance to
+ register VirtualAddress change callbacks. Among other things.
+
+ @retval EFI_SUCCESS Everything is good. Continue with init.
+ @retval Others Uh... don't continue.
+
+**/
+EFI_STATUS
+VariablePolicyExtraInit (
+ VOID
+ )
+{
+ // NULL implementation.
+ return EFI_SUCCESS;
+}
+
+/**
+ An extra deinit hook that enables the RuntimeDxe library instance to
+ register VirtualAddress change callbacks. Among other things.
+
+ @retval EFI_SUCCESS Everything is good. Continue with deinit.
+ @retval Others Uh... don't continue.
+
+**/
+EFI_STATUS
+VariablePolicyExtraDeinit (
+ VOID
+ )
+{
+ // NULL implementation.
+ return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitRuntimeDxe.c b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitRuntimeDxe.c
index ffd43dc549..b29c3c788b 100644
--- a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitRuntimeDxe.c
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitRuntimeDxe.c
@@ -1,84 +1,84 @@
-/** @file -- VariablePolicyExtraInitRuntimeDxe.c
-This file contains extra init and deinit routines that register and unregister
-VariableAddressChange callbacks.
-
-Copyright (c) Microsoft Corporation.
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include <Library/UefiBootServicesTableLib.h>
-#include <Library/UefiRuntimeServicesTableLib.h>
-
-extern EFI_GET_VARIABLE mGetVariableHelper;
-extern UINT8 *mPolicyTable;
-STATIC BOOLEAN mIsVirtualAddrConverted;
-STATIC EFI_EVENT mVariablePolicyLibVirtualAddressChangeEvent = NULL;
-
-/**
- For the RuntimeDxe version of this lib, convert internal pointer addresses to virtual addresses.
-
- @param[in] Event Event whose notification function is being invoked.
- @param[in] Context The pointer to the notification function's context, which
- is implementation-dependent.
-**/
-STATIC
-VOID
-EFIAPI
-VariablePolicyLibVirtualAddressCallback (
- IN EFI_EVENT Event,
- IN VOID *Context
- )
-{
- gRT->ConvertPointer (0, (VOID **)&mPolicyTable);
- gRT->ConvertPointer (0, (VOID **)&mGetVariableHelper);
- mIsVirtualAddrConverted = TRUE;
-}
-
-/**
- An extra init hook that enables the RuntimeDxe library instance to
- register VirtualAddress change callbacks. Among other things.
-
- @retval EFI_SUCCESS Everything is good. Continue with init.
- @retval Others Uh... don't continue.
-
-**/
-EFI_STATUS
-VariablePolicyExtraInit (
- VOID
- )
-{
- return gBS->CreateEventEx (
- EVT_NOTIFY_SIGNAL,
- TPL_NOTIFY,
- VariablePolicyLibVirtualAddressCallback,
- NULL,
- &gEfiEventVirtualAddressChangeGuid,
- &mVariablePolicyLibVirtualAddressChangeEvent
- );
-}
-
-/**
- An extra deinit hook that enables the RuntimeDxe library instance to
- register VirtualAddress change callbacks. Among other things.
-
- @retval EFI_SUCCESS Everything is good. Continue with deinit.
- @retval Others Uh... don't continue.
-
-**/
-EFI_STATUS
-VariablePolicyExtraDeinit (
- VOID
- )
-{
- EFI_STATUS Status;
-
- Status = EFI_SUCCESS;
- if (mIsVirtualAddrConverted) {
- Status = gBS->CloseEvent (mVariablePolicyLibVirtualAddressChangeEvent);
- } else {
- Status = EFI_SUCCESS;
- }
-
- return Status;
-}
+/** @file -- VariablePolicyExtraInitRuntimeDxe.c
+This file contains extra init and deinit routines that register and unregister
+VariableAddressChange callbacks.
+
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+extern EFI_GET_VARIABLE mGetVariableHelper;
+extern UINT8 *mPolicyTable;
+STATIC BOOLEAN mIsVirtualAddrConverted;
+STATIC EFI_EVENT mVariablePolicyLibVirtualAddressChangeEvent = NULL;
+
+/**
+ For the RuntimeDxe version of this lib, convert internal pointer addresses to virtual addresses.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context The pointer to the notification function's context, which
+ is implementation-dependent.
+**/
+STATIC
+VOID
+EFIAPI
+VariablePolicyLibVirtualAddressCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ gRT->ConvertPointer (0, (VOID **)&mPolicyTable);
+ gRT->ConvertPointer (0, (VOID **)&mGetVariableHelper);
+ mIsVirtualAddrConverted = TRUE;
+}
+
+/**
+ An extra init hook that enables the RuntimeDxe library instance to
+ register VirtualAddress change callbacks. Among other things.
+
+ @retval EFI_SUCCESS Everything is good. Continue with init.
+ @retval Others Uh... don't continue.
+
+**/
+EFI_STATUS
+VariablePolicyExtraInit (
+ VOID
+ )
+{
+ return gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ VariablePolicyLibVirtualAddressCallback,
+ NULL,
+ &gEfiEventVirtualAddressChangeGuid,
+ &mVariablePolicyLibVirtualAddressChangeEvent
+ );
+}
+
+/**
+ An extra deinit hook that enables the RuntimeDxe library instance to
+ register VirtualAddress change callbacks. Among other things.
+
+ @retval EFI_SUCCESS Everything is good. Continue with deinit.
+ @retval Others Uh... don't continue.
+
+**/
+EFI_STATUS
+VariablePolicyExtraDeinit (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+ if (mIsVirtualAddrConverted) {
+ Status = gBS->CloseEvent (mVariablePolicyLibVirtualAddressChangeEvent);
+ } else {
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c
index 768662829d..99b3b2a52a 100644
--- a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c
@@ -1,1167 +1,1167 @@
-/** @file -- VariablePolicyLib.c
-Business logic for Variable Policy enforcement.
-
-Copyright (c) Microsoft Corporation.
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include <Uefi.h>
-
-#include <Library/BaseLib.h>
-#include <Library/SafeIntLib.h>
-#include <Library/MemoryAllocationLib.h>
-#include <Library/BaseMemoryLib.h>
-#include <Library/DebugLib.h>
-#include <Library/PcdLib.h>
-
-#include <Protocol/VariablePolicy.h>
-#include <Library/VariablePolicyLib.h>
-
-// IMPORTANT NOTE: This library is currently rife with multiple return statements
-// for error handling. A refactor should remove these at some point.
-
-//
-// This library was designed with advanced unit-test features.
-// This define handles the configuration.
-#ifdef INTERNAL_UNIT_TEST
- #undef STATIC
-#define STATIC // Nothing...
-#endif
-
-// An abstracted GetVariable interface that enables configuration regardless of the environment.
-EFI_GET_VARIABLE mGetVariableHelper = NULL;
-
-// Master switch to lock this entire interface. Does not stop enforcement,
-// just prevents the configuration from being changed for the rest of the boot.
-STATIC BOOLEAN mInterfaceLocked = FALSE;
-
-// Master switch to disable the entire interface for a single boot.
-// This will disable all policy enforcement for the duration of the boot.
-STATIC BOOLEAN mProtectionDisabled = FALSE;
-
-// Table to hold all the current policies.
-UINT8 *mPolicyTable = NULL;
-STATIC UINT32 mCurrentTableSize = 0;
-STATIC UINT32 mCurrentTableUsage = 0;
-STATIC UINT32 mCurrentTableCount = 0;
-
-#define POLICY_TABLE_STEP_SIZE 0x1000
-
-// NOTE: DO NOT USE THESE MACROS on any structure that has not been validated.
-// Current table data has already been sanitized.
-#define GET_NEXT_POLICY(CurPolicy) (VARIABLE_POLICY_ENTRY*)((UINT8*)CurPolicy + CurPolicy->Size)
-#define GET_POLICY_NAME(CurPolicy) (CHAR16*)((UINTN)CurPolicy + CurPolicy->OffsetToName)
-
-#define MATCH_PRIORITY_EXACT 0
-#define MATCH_PRIORITY_MAX MATCH_PRIORITY_EXACT
-#define MATCH_PRIORITY_MIN MAX_UINT8
-
-/**
- An extra init hook that enables the RuntimeDxe library instance to
- register VirtualAddress change callbacks. Among other things.
-
- @retval EFI_SUCCESS Everything is good. Continue with init.
- @retval Others Uh... don't continue.
-
-**/
-EFI_STATUS
-VariablePolicyExtraInit (
- VOID
- );
-
-/**
- An extra deinit hook that enables the RuntimeDxe library instance to
- register VirtualAddress change callbacks. Among other things.
-
- @retval EFI_SUCCESS Everything is good. Continue with deinit.
- @retval Others Uh... don't continue.
-
-**/
-EFI_STATUS
-VariablePolicyExtraDeinit (
- VOID
- );
-
-/**
- This helper function determines whether the structure of an incoming policy
- is valid and internally consistent.
-
- @param[in] NewPolicy Pointer to the incoming policy structure.
-
- @retval TRUE
- @retval FALSE Pointer is NULL, size is wrong, strings are empty, or
- substructures overlap.
-
-**/
-STATIC
-BOOLEAN
-IsValidVariablePolicyStructure (
- IN CONST VARIABLE_POLICY_ENTRY *NewPolicy
- )
-{
- EFI_STATUS Status;
- UINTN EntryEnd;
- CHAR16 *CheckChar;
- UINTN WildcardCount;
-
- // Sanitize some quick values.
- if ((NewPolicy == NULL) || (NewPolicy->Size == 0) ||
- // Structure size should be at least as long as the minumum structure and a NULL string.
- (NewPolicy->Size < sizeof (VARIABLE_POLICY_ENTRY)) ||
- // Check for the known revision.
- (NewPolicy->Version != VARIABLE_POLICY_ENTRY_REVISION))
- {
- return FALSE;
- }
-
- // Calculate the theoretical end of the structure and make sure
- // that the structure can fit in memory.
- Status = SafeUintnAdd ((UINTN)NewPolicy, NewPolicy->Size, &EntryEnd);
- if (EFI_ERROR (Status)) {
- return FALSE;
- }
-
- // Check for a valid Max Size.
- if (NewPolicy->MaxSize == 0) {
- return FALSE;
- }
-
- // Check for the valid list of lock policies.
- if ((NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_NO_LOCK) &&
- (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_NOW) &&
- (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) &&
- (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE))
- {
- return FALSE;
- }
-
- // If the policy type is VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE, make sure that the matching state variable Name
- // terminates before the OffsetToName for the matching policy variable Name.
- if (NewPolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {
- // Adjust CheckChar to the offset of the LockPolicy->Name.
- Status = SafeUintnAdd (
- (UINTN)NewPolicy + sizeof (VARIABLE_POLICY_ENTRY),
- sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY),
- (UINTN *)&CheckChar
- );
- if (EFI_ERROR (Status) || (EntryEnd <= (UINTN)CheckChar)) {
- return FALSE;
- }
-
- while (*CheckChar != CHAR_NULL) {
- if (EntryEnd <= (UINTN)CheckChar) {
- return FALSE;
- }
-
- CheckChar++;
- }
-
- // At this point we should have either exeeded the structure or be pointing at the last char in LockPolicy->Name.
- // We should check to make sure that the policy Name comes immediately after this charcter.
- if ((UINTN)++ CheckChar != (UINTN)NewPolicy + NewPolicy->OffsetToName) {
- return FALSE;
- }
-
- // If the policy type is any other value, make sure that the LockPolicy structure has a zero length.
- } else {
- if (NewPolicy->OffsetToName != sizeof (VARIABLE_POLICY_ENTRY)) {
- return FALSE;
- }
- }
-
- // Check to make sure that the name has a terminating character
- // before the end of the structure.
- // We've already checked that the name is within the bounds of the structure.
- if (NewPolicy->Size != NewPolicy->OffsetToName) {
- CheckChar = (CHAR16 *)((UINTN)NewPolicy + NewPolicy->OffsetToName);
- WildcardCount = 0;
- while (*CheckChar != CHAR_NULL) {
- // Make sure there aren't excessive wildcards.
- if (*CheckChar == '#') {
- WildcardCount++;
- if (WildcardCount > MATCH_PRIORITY_MIN) {
- return FALSE;
- }
- }
-
- // Make sure you're still within the bounds of the policy structure.
- if (EntryEnd <= (UINTN)CheckChar) {
- return FALSE;
- }
-
- CheckChar++;
- }
-
- // Finally, we should be pointed at the very last character in Name, so we should be right
- // up against the end of the structure.
- if ((UINTN)++ CheckChar != EntryEnd) {
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-/**
- This helper function evaluates a policy and determines whether it matches the target
- variable. If matched, will also return a value corresponding to the priority of the match.
-
- The rules for "best match" are listed in the Variable Policy Spec.
- Perfect name matches will return 0.
- Single wildcard characters will return the number of wildcard characters.
- Full namespaces will return MAX_UINT8.
-
- @param[in] EvalEntry Pointer to the policy entry being evaluated.
- @param[in] VariableName Same as EFI_SET_VARIABLE.
- @param[in] VendorGuid Same as EFI_SET_VARIABLE.
- @param[out] MatchPriority [Optional] On finding a match, this value contains the priority of the match.
- Lower number == higher priority. Only valid if a match found.
-
- @retval TRUE Current entry matches the target variable.
- @retval FALSE Current entry does not match at all.
-
-**/
-STATIC
-BOOLEAN
-EvaluatePolicyMatch (
- IN CONST VARIABLE_POLICY_ENTRY *EvalEntry,
- IN CONST CHAR16 *VariableName,
- IN CONST EFI_GUID *VendorGuid,
- OUT UINT8 *MatchPriority OPTIONAL
- )
-{
- BOOLEAN Result;
- CHAR16 *PolicyName;
- UINT8 CalculatedPriority;
- UINTN Index;
-
- Result = FALSE;
- CalculatedPriority = MATCH_PRIORITY_EXACT;
-
- // Step 1: If the GUID doesn't match, we're done. No need to evaluate anything else.
- if (!CompareGuid (&EvalEntry->Namespace, VendorGuid)) {
- goto Exit;
- }
-
- // If the GUID matches, check to see whether there is a Name associated
- // with the policy. If not, this policy matches the entire namespace.
- // Missing Name is indicated by size being equal to name.
- if (EvalEntry->Size == EvalEntry->OffsetToName) {
- CalculatedPriority = MATCH_PRIORITY_MIN;
- Result = TRUE;
- goto Exit;
- }
-
- // Now that we know the name exists, get it.
- PolicyName = GET_POLICY_NAME (EvalEntry);
-
- // Evaluate the name against the policy name and check for a match.
- // Account for any wildcards.
- Index = 0;
- Result = TRUE;
- // Keep going until the end of both strings.
- while (PolicyName[Index] != CHAR_NULL || VariableName[Index] != CHAR_NULL) {
- // If we don't have a match...
- if ((PolicyName[Index] != VariableName[Index]) || (PolicyName[Index] == '#')) {
- // If this is a numerical wildcard, we can consider
- // it a match if we alter the priority.
- if ((PolicyName[Index] == L'#') &&
- (((L'0' <= VariableName[Index]) && (VariableName[Index] <= L'9')) ||
- ((L'A' <= VariableName[Index]) && (VariableName[Index] <= L'F')) ||
- ((L'a' <= VariableName[Index]) && (VariableName[Index] <= L'f'))))
- {
- if (CalculatedPriority < MATCH_PRIORITY_MIN) {
- CalculatedPriority++;
- }
-
- // Otherwise, not a match.
- } else {
- Result = FALSE;
- goto Exit;
- }
- }
-
- Index++;
- }
-
-Exit:
- if (Result && (MatchPriority != NULL)) {
- *MatchPriority = CalculatedPriority;
- }
-
- return Result;
-}
-
-/**
- This helper function walks the current policy table and returns a pointer
- to the best match, if any are found. Leverages EvaluatePolicyMatch() to
- determine "best".
-
- @param[in] VariableName Same as EFI_SET_VARIABLE.
- @param[in] VendorGuid Same as EFI_SET_VARIABLE.
- @param[out] ReturnPriority [Optional] If pointer is provided, return the
- priority of the match. Same as EvaluatePolicyMatch().
- Only valid if a match is returned.
-
- @retval VARIABLE_POLICY_ENTRY* Best match that was found.
- @retval NULL No match was found.
-
-**/
-STATIC
-VARIABLE_POLICY_ENTRY *
-GetBestPolicyMatch (
- IN CONST CHAR16 *VariableName,
- IN CONST EFI_GUID *VendorGuid,
- OUT UINT8 *ReturnPriority OPTIONAL
- )
-{
- VARIABLE_POLICY_ENTRY *BestResult;
- VARIABLE_POLICY_ENTRY *CurrentEntry;
- UINT8 MatchPriority;
- UINT8 CurrentPriority;
- UINTN Index;
-
- BestResult = NULL;
- MatchPriority = MATCH_PRIORITY_EXACT;
-
- // Walk all entries in the table, looking for matches.
- CurrentEntry = (VARIABLE_POLICY_ENTRY *)mPolicyTable;
- for (Index = 0; Index < mCurrentTableCount; Index++) {
- // Check for a match.
- if (EvaluatePolicyMatch (CurrentEntry, VariableName, VendorGuid, &CurrentPriority)) {
- // If match is better, take it.
- if ((BestResult == NULL) || (CurrentPriority < MatchPriority)) {
- BestResult = CurrentEntry;
- MatchPriority = CurrentPriority;
- }
-
- // If you've hit the highest-priority match, can exit now.
- if (MatchPriority == 0) {
- break;
- }
- }
-
- // If we're still in the loop, move to the next entry.
- CurrentEntry = GET_NEXT_POLICY (CurrentEntry);
- }
-
- // If a return priority was requested, return it.
- if (ReturnPriority != NULL) {
- *ReturnPriority = MatchPriority;
- }
-
- return BestResult;
-}
-
-/**
- This API function validates and registers a new policy with
- the policy enforcement engine.
-
- @param[in] NewPolicy Pointer to the incoming policy structure.
-
- @retval EFI_SUCCESS
- @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.
- @retval EFI_ALREADY_STARTED An identical matching policy already exists.
- @retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.
- @retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies.
- @retval EFI_ABORTED A calculation error has prevented this function from completing.
- @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.
- @retval EFI_NOT_READY Library has not yet been initialized.
-
-**/
-EFI_STATUS
-EFIAPI
-RegisterVariablePolicy (
- IN CONST VARIABLE_POLICY_ENTRY *NewPolicy
- )
-{
- EFI_STATUS Status;
- VARIABLE_POLICY_ENTRY *MatchPolicy;
- UINT8 MatchPriority;
- UINT32 NewSize;
- UINT8 *NewTable;
-
- if (!IsVariablePolicyLibInitialized ()) {
- return EFI_NOT_READY;
- }
-
- if (mInterfaceLocked) {
- return EFI_WRITE_PROTECTED;
- }
-
- if (!IsValidVariablePolicyStructure (NewPolicy)) {
- return EFI_INVALID_PARAMETER;
- }
-
- // Check to see whether an exact matching policy already exists.
- MatchPolicy = GetBestPolicyMatch (
- GET_POLICY_NAME (NewPolicy),
- &NewPolicy->Namespace,
- &MatchPriority
- );
- if ((MatchPolicy != NULL) && (MatchPriority == MATCH_PRIORITY_EXACT)) {
- return EFI_ALREADY_STARTED;
- }
-
- // If none exists, create it.
- // If we need more space, allocate that now.
- Status = SafeUint32Add (mCurrentTableUsage, NewPolicy->Size, &NewSize);
- if (EFI_ERROR (Status)) {
- return EFI_ABORTED;
- }
-
- if (NewSize > mCurrentTableSize) {
- // Use NewSize to calculate the new table size in units of POLICY_TABLE_STEP_SIZE.
- NewSize = (NewSize % POLICY_TABLE_STEP_SIZE) > 0 ?
- (NewSize / POLICY_TABLE_STEP_SIZE) + 1 :
- (NewSize / POLICY_TABLE_STEP_SIZE);
- // Calculate the new table size in absolute bytes.
- Status = SafeUint32Mult (NewSize, POLICY_TABLE_STEP_SIZE, &NewSize);
- if (EFI_ERROR (Status)) {
- return EFI_ABORTED;
- }
-
- // Reallocate and copy the table.
- NewTable = AllocateRuntimePool (NewSize);
- if (NewTable == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- CopyMem (NewTable, mPolicyTable, mCurrentTableUsage);
- mCurrentTableSize = NewSize;
- if (mPolicyTable != NULL) {
- FreePool (mPolicyTable);
- }
-
- mPolicyTable = NewTable;
- }
-
- // Copy the policy into the table.
- CopyMem (mPolicyTable + mCurrentTableUsage, NewPolicy, NewPolicy->Size);
- mCurrentTableUsage += NewPolicy->Size;
- mCurrentTableCount += 1;
-
- // We're done here.
-
- return EFI_SUCCESS;
-}
-
-/**
- This API function checks to see whether the parameters to SetVariable would
- be allowed according to the current variable policies.
-
- @param[in] VariableName Same as EFI_SET_VARIABLE.
- @param[in] VendorGuid Same as EFI_SET_VARIABLE.
- @param[in] Attributes Same as EFI_SET_VARIABLE.
- @param[in] DataSize Same as EFI_SET_VARIABLE.
- @param[in] Data Same as EFI_SET_VARIABLE.
-
- @retval EFI_SUCCESS A matching policy allows this update.
- @retval EFI_SUCCESS There are currently no policies that restrict this update.
- @retval EFI_SUCCESS The protections have been disable until the next reboot.
- @retval EFI_WRITE_PROTECTED Variable is currently locked.
- @retval EFI_INVALID_PARAMETER Attributes or size are invalid.
- @retval EFI_ABORTED A lock policy exists, but an error prevented evaluation.
- @retval EFI_NOT_READY Library has not been initialized.
-
-**/
-EFI_STATUS
-EFIAPI
-ValidateSetVariable (
- IN CHAR16 *VariableName,
- IN EFI_GUID *VendorGuid,
- IN UINT32 Attributes,
- IN UINTN DataSize,
- IN VOID *Data
- )
-{
- BOOLEAN IsDel;
- VARIABLE_POLICY_ENTRY *ActivePolicy;
- EFI_STATUS Status;
- EFI_STATUS ReturnStatus;
- VARIABLE_LOCK_ON_VAR_STATE_POLICY *StateVarPolicy;
- CHAR16 *StateVarName;
- UINTN StateVarSize;
- UINT8 StateVar;
-
- ReturnStatus = EFI_SUCCESS;
-
- if (!IsVariablePolicyLibInitialized ()) {
- ReturnStatus = EFI_NOT_READY;
- goto Exit;
- }
-
- // Bail if the protections are currently disabled.
- if (mProtectionDisabled) {
- ReturnStatus = EFI_SUCCESS;
- goto Exit;
- }
-
- // Determine whether this is a delete operation.
- // If so, it will affect which tests are applied.
- if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {
- IsDel = TRUE;
- } else {
- IsDel = FALSE;
- }
-
- // Find an active policy if one exists.
- ActivePolicy = GetBestPolicyMatch (VariableName, VendorGuid, NULL);
-
- // If we have an active policy, check it against the incoming data.
- if (ActivePolicy != NULL) {
- //
- // Only enforce size and attribute constraints when updating data, not deleting.
- if (!IsDel) {
- // Check for size constraints.
- if (((ActivePolicy->MinSize > 0) && (DataSize < ActivePolicy->MinSize)) ||
- ((ActivePolicy->MaxSize > 0) && (DataSize > ActivePolicy->MaxSize)))
- {
- ReturnStatus = EFI_INVALID_PARAMETER;
- DEBUG ((
- DEBUG_VERBOSE,
- "%a - Bad Size. 0x%X <> 0x%X-0x%X\n",
- __func__,
- DataSize,
- ActivePolicy->MinSize,
- ActivePolicy->MaxSize
- ));
- goto Exit;
- }
-
- // Check for attribute constraints.
- if (((ActivePolicy->AttributesMustHave & Attributes) != ActivePolicy->AttributesMustHave) ||
- ((ActivePolicy->AttributesCantHave & Attributes) != 0))
- {
- ReturnStatus = EFI_INVALID_PARAMETER;
- DEBUG ((
- DEBUG_VERBOSE,
- "%a - Bad Attributes. 0x%X <> 0x%X:0x%X\n",
- __func__,
- Attributes,
- ActivePolicy->AttributesMustHave,
- ActivePolicy->AttributesCantHave
- ));
- goto Exit;
- }
- }
-
- //
- // Lock policy check.
- //
- // Check for immediate lock.
- if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_NOW) {
- ReturnStatus = EFI_WRITE_PROTECTED;
- goto Exit;
- // Check for lock on create.
- } else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) {
- StateVarSize = 0;
- Status = mGetVariableHelper (
- VariableName,
- VendorGuid,
- NULL,
- &StateVarSize,
- NULL
- );
- if (Status == EFI_BUFFER_TOO_SMALL) {
- ReturnStatus = EFI_WRITE_PROTECTED;
- goto Exit;
- }
-
- // Check for lock on state variable.
- } else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {
- StateVarPolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY *)((UINT8 *)ActivePolicy + sizeof (VARIABLE_POLICY_ENTRY));
- StateVarName = (CHAR16 *)((UINT8 *)StateVarPolicy + sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY));
- StateVarSize = sizeof (StateVar);
- Status = mGetVariableHelper (
- StateVarName,
- &StateVarPolicy->Namespace,
- NULL,
- &StateVarSize,
- &StateVar
- );
-
- // If the variable was found, check the state. If matched, this variable is locked.
- if (!EFI_ERROR (Status)) {
- if (StateVar == StateVarPolicy->Value) {
- ReturnStatus = EFI_WRITE_PROTECTED;
- goto Exit;
- }
-
- // EFI_NOT_FOUND and EFI_BUFFER_TOO_SMALL indicate that the state doesn't match.
- } else if ((Status != EFI_NOT_FOUND) && (Status != EFI_BUFFER_TOO_SMALL)) {
- // We don't know what happened, but it isn't good.
- ReturnStatus = EFI_ABORTED;
- goto Exit;
- }
- }
- }
-
-Exit:
- DEBUG ((DEBUG_VERBOSE, "%a - Variable (%g:%s) returning %r.\n", __func__, VendorGuid, VariableName, ReturnStatus));
- return ReturnStatus;
-}
-
-/**
- This API function disables the variable policy enforcement. If it's
- already been called once, will return EFI_ALREADY_STARTED.
-
- @retval EFI_SUCCESS
- @retval EFI_ALREADY_STARTED Has already been called once this boot.
- @retval EFI_WRITE_PROTECTED Interface has been locked until reboot.
- @retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.
- @retval EFI_NOT_READY Library has not yet been initialized.
-
-**/
-EFI_STATUS
-EFIAPI
-DisableVariablePolicy (
- VOID
- )
-{
- if (!IsVariablePolicyLibInitialized ()) {
- return EFI_NOT_READY;
- }
-
- if (mProtectionDisabled) {
- return EFI_ALREADY_STARTED;
- }
-
- if (mInterfaceLocked) {
- return EFI_WRITE_PROTECTED;
- }
-
- if (!PcdGetBool (PcdAllowVariablePolicyEnforcementDisable)) {
- return EFI_WRITE_PROTECTED;
- }
-
- mProtectionDisabled = TRUE;
- return EFI_SUCCESS;
-}
-
-/**
- This API function will dump the entire contents of the variable policy table.
-
- Similar to GetVariable, the first call can be made with a 0 size and it will return
- the size of the buffer required to hold the entire table.
-
- @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.
- @param[in,out] Size On input, the size of the output buffer. On output, the size
- of the data returned.
-
- @retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.
- @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.
- @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.
- @retval EFI_NOT_READY Library has not yet been initialized.
-
-**/
-EFI_STATUS
-EFIAPI
-DumpVariablePolicy (
- OUT UINT8 *Policy,
- IN OUT UINT32 *Size
- )
-{
- if (!IsVariablePolicyLibInitialized ()) {
- return EFI_NOT_READY;
- }
-
- // Check the parameters.
- if ((Size == NULL) || ((*Size > 0) && (Policy == NULL))) {
- return EFI_INVALID_PARAMETER;
- }
-
- // Make sure the size is sufficient to hold the policy table.
- if (*Size < mCurrentTableUsage) {
- *Size = mCurrentTableUsage;
- return EFI_BUFFER_TOO_SMALL;
- }
-
- // If we're still here, copy the table and bounce.
- CopyMem (Policy, mPolicyTable, mCurrentTableUsage);
- *Size = mCurrentTableUsage;
-
- return EFI_SUCCESS;
-}
-
-/**
- This function will return variable policy information for a UEFI variable with a
- registered variable policy.
-
- @param[in] VariableName The name of the variable to use for the policy search.
- @param[in] VendorGuid The vendor GUID of the variable to use for the policy search.
- @param[in,out] VariablePolicyVariableNameBufferSize On input, the size, in bytes, of the VariablePolicyVariableName
- buffer.
-
- On output, the size, in bytes, needed to store the variable
- policy variable name.
-
- If testing for the VariablePolicyVariableName buffer size
- needed, set this value to zero so EFI_BUFFER_TOO_SMALL is
- guaranteed to be returned if the variable policy variable name
- is found.
- @param[out] VariablePolicy Pointer to a buffer where the policy entry will be written
- if found.
- @param[out] VariablePolicyVariableName Pointer to a buffer where the variable name used for the
- variable policy will be written if a variable name is
- registered.
-
- If the variable policy is not associated with a variable name
- (e.g. applied to variable vendor namespace) and this parameter
- is given, this parameter will not be modified and
- VariablePolicyVariableNameBufferSize will be set to zero to
- indicate a name was not present.
-
- If the pointer given is not NULL,
- VariablePolicyVariableNameBufferSize must be non-NULL.
-
- @retval EFI_SUCCESS A variable policy entry was found and returned successfully.
- @retval EFI_BAD_BUFFER_SIZE An internal buffer size caused a calculation error.
- @retval EFI_BUFFER_TOO_SMALL The VariablePolicyVariableName buffer value is too small for the size needed.
- The buffer should now point to the size needed.
- @retval EFI_NOT_READY Variable policy has not yet been initialized.
- @retval EFI_INVALID_PARAMETER A required pointer argument passed is NULL. This will be returned if
- VariablePolicyVariableName is non-NULL and VariablePolicyVariableNameBufferSize
- is NULL.
- @retval EFI_NOT_FOUND A variable policy was not found for the given UEFI variable name and vendor GUID.
-
-**/
-EFI_STATUS
-EFIAPI
-GetVariablePolicyInfo (
- IN CONST CHAR16 *VariableName,
- IN CONST EFI_GUID *VendorGuid,
- IN OUT UINTN *VariablePolicyVariableNameBufferSize OPTIONAL,
- OUT VARIABLE_POLICY_ENTRY *VariablePolicy,
- OUT CHAR16 *VariablePolicyVariableName OPTIONAL
- )
-{
- EFI_STATUS Status;
- UINT8 MatchPriority;
- UINTN LocalVariablePolicyVariableNameBufferSize;
- UINTN RequiredVariablePolicyVariableNameBufferSize;
- VARIABLE_POLICY_ENTRY *MatchPolicy;
-
- Status = EFI_SUCCESS;
-
- if (!IsVariablePolicyLibInitialized ()) {
- return EFI_NOT_READY;
- }
-
- if ((VariableName == NULL) || (VendorGuid == NULL) || (VariablePolicy == NULL)) {
- return EFI_INVALID_PARAMETER;
- }
-
- MatchPolicy = GetBestPolicyMatch (
- VariableName,
- VendorGuid,
- &MatchPriority
- );
- if (MatchPolicy != NULL) {
- CopyMem (VariablePolicy, MatchPolicy, sizeof (*VariablePolicy));
-
- if (VariablePolicyVariableNameBufferSize == NULL) {
- if (VariablePolicyVariableName != NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- return Status;
- }
-
- if (MatchPolicy->Size != MatchPolicy->OffsetToName) {
- if (MatchPolicy->Size < MatchPolicy->OffsetToName) {
- ASSERT (MatchPolicy->Size > MatchPolicy->OffsetToName);
- return EFI_BAD_BUFFER_SIZE;
- }
-
- RequiredVariablePolicyVariableNameBufferSize = (UINTN)(MatchPolicy->Size - MatchPolicy->OffsetToName);
- ASSERT (RequiredVariablePolicyVariableNameBufferSize > 0);
-
- if (*VariablePolicyVariableNameBufferSize < RequiredVariablePolicyVariableNameBufferSize) {
- // Let the caller get the size needed to hold the policy variable name
- *VariablePolicyVariableNameBufferSize = RequiredVariablePolicyVariableNameBufferSize;
- return EFI_BUFFER_TOO_SMALL;
- }
-
- if (VariablePolicyVariableName == NULL) {
- // If the policy variable name size given is valid, then a valid policy variable name buffer should be provided
- *VariablePolicyVariableNameBufferSize = RequiredVariablePolicyVariableNameBufferSize;
- return EFI_INVALID_PARAMETER;
- }
-
- LocalVariablePolicyVariableNameBufferSize = *VariablePolicyVariableNameBufferSize;
-
- // Actual string size should match expected string size
- if (
- ((StrnLenS (GET_POLICY_NAME (MatchPolicy), RequiredVariablePolicyVariableNameBufferSize) + 1) * sizeof (CHAR16))
- != RequiredVariablePolicyVariableNameBufferSize)
- {
- ASSERT_EFI_ERROR (EFI_BAD_BUFFER_SIZE);
- return EFI_BAD_BUFFER_SIZE;
- }
-
- *VariablePolicyVariableNameBufferSize = RequiredVariablePolicyVariableNameBufferSize;
-
- Status = StrnCpyS (
- VariablePolicyVariableName,
- LocalVariablePolicyVariableNameBufferSize / sizeof (CHAR16),
- GET_POLICY_NAME (MatchPolicy),
- RequiredVariablePolicyVariableNameBufferSize / sizeof (CHAR16)
- );
- ASSERT_EFI_ERROR (Status);
- } else {
- // A variable policy variable name is not present. Return values according to interface.
- *VariablePolicyVariableNameBufferSize = 0;
- }
-
- return Status;
- }
-
- return EFI_NOT_FOUND;
-}
-
-/**
- This function will return the Lock on Variable State policy information for the policy
- associated with the given UEFI variable.
-
- @param[in] VariableName The name of the variable to use for the policy search.
- @param[in] VendorGuid The vendor GUID of the variable to use for the policy
- search.
- @param[in,out] VariableLockPolicyVariableNameBufferSize On input, the size, in bytes, of the
- VariableLockPolicyVariableName buffer.
-
- On output, the size, in bytes, needed to store the variable
- policy variable name.
-
- If testing for the VariableLockPolicyVariableName buffer
- size needed, set this value to zero so EFI_BUFFER_TOO_SMALL
- is guaranteed to be returned if the variable policy variable
- name is found.
- @param[out] VariablePolicy Pointer to a buffer where the policy entry will be written
- if found.
- @param[out] VariableLockPolicyVariableName Pointer to a buffer where the variable name used for the
- variable lock on variable state policy will be written if
- a variable name is registered.
-
- If the lock on variable policy is not associated with a
- variable name (e.g. applied to variable vendor namespace)
- and this parameter is given, this parameter will not be
- modified and VariableLockPolicyVariableNameBufferSize will
- be set to zero to indicate a name was not present.
-
- If the pointer given is not NULL,
- VariableLockPolicyVariableNameBufferSize must be non-NULL.
-
- @retval EFI_SUCCESS A Lock on Variable State variable policy entry was found and returned
- successfully.
- @retval EFI_BAD_BUFFER_SIZE An internal buffer size caused a calculation error.
- @retval EFI_BUFFER_TOO_SMALL The VariableLockPolicyVariableName buffer is too small for the size needed.
- The buffer should now point to the size needed.
- @retval EFI_NOT_READY Variable policy has not yet been initialized.
- @retval EFI_INVALID_PARAMETER A required pointer argument passed is NULL. This will be returned if
- VariableLockPolicyVariableName is non-NULL and
- VariableLockPolicyVariableNameBufferSize is NULL.
- @retval EFI_NOT_FOUND A Lock on Variable State variable policy was not found for the given UEFI
- variable name and vendor GUID.
-
-**/
-EFI_STATUS
-EFIAPI
-GetLockOnVariableStateVariablePolicyInfo (
- IN CONST CHAR16 *VariableName,
- IN CONST EFI_GUID *VendorGuid,
- IN OUT UINTN *VariableLockPolicyVariableNameBufferSize OPTIONAL,
- OUT VARIABLE_LOCK_ON_VAR_STATE_POLICY *VariablePolicy,
- OUT CHAR16 *VariableLockPolicyVariableName OPTIONAL
- )
-{
- EFI_STATUS Status;
- UINT8 MatchPriority;
- UINTN RequiredVariablePolicyVariableNameBufferSize;
- UINTN RequiredVariableLockPolicyVariableNameBufferSize;
- UINTN LocalVariablePolicyLockVariableNameBufferSize;
- UINTN LockOnVarStatePolicyEndOffset;
- CHAR16 *LocalVariableLockPolicyVariableName;
- VARIABLE_LOCK_ON_VAR_STATE_POLICY *LocalLockOnVarStatePolicy;
- VARIABLE_POLICY_ENTRY *MatchPolicy;
-
- Status = EFI_SUCCESS;
-
- if (!IsVariablePolicyLibInitialized ()) {
- return EFI_NOT_READY;
- }
-
- if ((VariableName == NULL) || (VendorGuid == NULL) || (VariablePolicy == NULL)) {
- return EFI_INVALID_PARAMETER;
- }
-
- MatchPolicy = GetBestPolicyMatch (
- VariableName,
- VendorGuid,
- &MatchPriority
- );
- if (MatchPolicy != NULL) {
- if (MatchPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {
- return EFI_NOT_FOUND;
- }
-
- Status = SafeUintnAdd (
- sizeof (VARIABLE_POLICY_ENTRY),
- sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY),
- &LockOnVarStatePolicyEndOffset
- );
- if (EFI_ERROR (Status) || (LockOnVarStatePolicyEndOffset > (UINTN)MatchPolicy->Size)) {
- return EFI_BAD_BUFFER_SIZE;
- }
-
- LocalLockOnVarStatePolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY *)(MatchPolicy + 1);
- CopyMem (VariablePolicy, LocalLockOnVarStatePolicy, sizeof (*LocalLockOnVarStatePolicy));
-
- if ((VariableLockPolicyVariableNameBufferSize == NULL)) {
- if (VariableLockPolicyVariableName != NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- return Status;
- }
-
- // The name offset should be less than or equal to the total policy size.
- if (MatchPolicy->Size < MatchPolicy->OffsetToName) {
- return EFI_BAD_BUFFER_SIZE;
- }
-
- RequiredVariablePolicyVariableNameBufferSize = (UINTN)(MatchPolicy->Size - MatchPolicy->OffsetToName);
- RequiredVariableLockPolicyVariableNameBufferSize = MatchPolicy->Size -
- (LockOnVarStatePolicyEndOffset + RequiredVariablePolicyVariableNameBufferSize);
-
- LocalVariablePolicyLockVariableNameBufferSize = *VariableLockPolicyVariableNameBufferSize;
- *VariableLockPolicyVariableNameBufferSize = RequiredVariableLockPolicyVariableNameBufferSize;
-
- if (LocalVariablePolicyLockVariableNameBufferSize < RequiredVariableLockPolicyVariableNameBufferSize) {
- // Let the caller get the size needed to hold the policy variable name
- return EFI_BUFFER_TOO_SMALL;
- }
-
- if (VariableLockPolicyVariableName == NULL) {
- // If the policy variable name size given is valid, then a valid policy variable name buffer should be provided
- return EFI_INVALID_PARAMETER;
- }
-
- if (RequiredVariableLockPolicyVariableNameBufferSize == 0) {
- return Status;
- }
-
- LocalVariableLockPolicyVariableName = (CHAR16 *)((UINT8 *)LocalLockOnVarStatePolicy + sizeof (*LocalLockOnVarStatePolicy));
- *VariableLockPolicyVariableNameBufferSize = RequiredVariableLockPolicyVariableNameBufferSize;
-
- // Actual string size should match expected string size (if a variable name is present)
- if (
- (RequiredVariablePolicyVariableNameBufferSize > 0) &&
- (((StrnLenS (GET_POLICY_NAME (MatchPolicy), RequiredVariablePolicyVariableNameBufferSize) + 1) * sizeof (CHAR16)) !=
- RequiredVariablePolicyVariableNameBufferSize))
- {
- ASSERT_EFI_ERROR (EFI_BAD_BUFFER_SIZE);
- return EFI_BAD_BUFFER_SIZE;
- }
-
- // Actual string size should match expected string size (if here, variable lock variable name is present)
- if (
- ((StrnLenS (LocalVariableLockPolicyVariableName, RequiredVariableLockPolicyVariableNameBufferSize) + 1) * sizeof (CHAR16)) !=
- RequiredVariableLockPolicyVariableNameBufferSize)
- {
- ASSERT_EFI_ERROR (EFI_BAD_BUFFER_SIZE);
- return EFI_BAD_BUFFER_SIZE;
- }
-
- Status = StrnCpyS (
- VariableLockPolicyVariableName,
- LocalVariablePolicyLockVariableNameBufferSize / sizeof (CHAR16),
- LocalVariableLockPolicyVariableName,
- RequiredVariableLockPolicyVariableNameBufferSize / sizeof (CHAR16)
- );
- ASSERT_EFI_ERROR (Status);
-
- return Status;
- }
-
- return EFI_NOT_FOUND;
-}
-
-/**
- This API function returns whether or not the policy engine is
- currently being enforced.
-
- @retval TRUE
- @retval FALSE
- @retval FALSE Library has not yet been initialized.
-
-**/
-BOOLEAN
-EFIAPI
-IsVariablePolicyEnabled (
- VOID
- )
-{
- if (!IsVariablePolicyLibInitialized ()) {
- return FALSE;
- }
-
- return !mProtectionDisabled;
-}
-
-/**
- This API function locks the interface so that no more policy updates
- can be performed or changes made to the enforcement until the next boot.
-
- @retval EFI_SUCCESS
- @retval EFI_NOT_READY Library has not yet been initialized.
-
-**/
-EFI_STATUS
-EFIAPI
-LockVariablePolicy (
- VOID
- )
-{
- if (!IsVariablePolicyLibInitialized ()) {
- return EFI_NOT_READY;
- }
-
- if (mInterfaceLocked) {
- return EFI_WRITE_PROTECTED;
- }
-
- mInterfaceLocked = TRUE;
- return EFI_SUCCESS;
-}
-
-/**
- This API function returns whether or not the policy interface is locked
- for the remainder of the boot.
-
- @retval TRUE
- @retval FALSE
- @retval FALSE Library has not yet been initialized.
-
-**/
-BOOLEAN
-EFIAPI
-IsVariablePolicyInterfaceLocked (
- VOID
- )
-{
- if (!IsVariablePolicyLibInitialized ()) {
- return FALSE;
- }
-
- return mInterfaceLocked;
-}
-
-/**
- This helper function initializes the library and sets
- up any required internal structures or handlers.
-
- Also registers the internal pointer for the GetVariable helper.
-
- @param[in] GetVariableHelper A function pointer matching the EFI_GET_VARIABLE prototype that will be used to
- check policy criteria that involve the existence of other variables.
-
- @retval EFI_SUCCESS
- @retval EFI_ALREADY_STARTED The initialize function has been called more than once without a call to
- deinitialize.
-
-**/
-EFI_STATUS
-EFIAPI
-InitVariablePolicyLib (
- IN EFI_GET_VARIABLE GetVariableHelper
- )
-{
- EFI_STATUS Status;
-
- Status = EFI_SUCCESS;
-
- if (mGetVariableHelper != NULL) {
- return EFI_ALREADY_STARTED;
- }
-
- if (!EFI_ERROR (Status)) {
- Status = VariablePolicyExtraInit ();
- }
-
- if (!EFI_ERROR (Status)) {
- // Save an internal pointer to the GetVariableHelper.
- mGetVariableHelper = GetVariableHelper;
-
- // Initialize the global state.
- mInterfaceLocked = FALSE;
- mProtectionDisabled = FALSE;
- mPolicyTable = NULL;
- mCurrentTableSize = 0;
- mCurrentTableUsage = 0;
- mCurrentTableCount = 0;
- }
-
- return Status;
-}
-
-/**
- This helper function returns whether or not the library is currently initialized.
-
- @retval TRUE
- @retval FALSE
-
-**/
-BOOLEAN
-EFIAPI
-IsVariablePolicyLibInitialized (
- VOID
- )
-{
- return (mGetVariableHelper != NULL);
-}
-
-/**
- This helper function tears down the library.
-
- Should generally only be used for test harnesses.
-
- @retval EFI_SUCCESS
- @retval EFI_NOT_READY Deinitialize was called without first calling initialize.
-
-**/
-EFI_STATUS
-EFIAPI
-DeinitVariablePolicyLib (
- VOID
- )
-{
- EFI_STATUS Status;
-
- Status = EFI_SUCCESS;
-
- if (mGetVariableHelper == NULL) {
- return EFI_NOT_READY;
- }
-
- if (!EFI_ERROR (Status)) {
- Status = VariablePolicyExtraDeinit ();
- }
-
- if (!EFI_ERROR (Status)) {
- mGetVariableHelper = NULL;
- mInterfaceLocked = FALSE;
- mProtectionDisabled = FALSE;
- mCurrentTableSize = 0;
- mCurrentTableUsage = 0;
- mCurrentTableCount = 0;
-
- if (mPolicyTable != NULL) {
- FreePool (mPolicyTable);
- mPolicyTable = NULL;
- }
- }
-
- return Status;
-}
+/** @file -- VariablePolicyLib.c
+Business logic for Variable Policy enforcement.
+
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+
+#include <Library/BaseLib.h>
+#include <Library/SafeIntLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+
+#include <Protocol/VariablePolicy.h>
+#include <Library/VariablePolicyLib.h>
+
+// IMPORTANT NOTE: This library is currently rife with multiple return statements
+// for error handling. A refactor should remove these at some point.
+
+//
+// This library was designed with advanced unit-test features.
+// This define handles the configuration.
+#ifdef INTERNAL_UNIT_TEST
+ #undef STATIC
+#define STATIC // Nothing...
+#endif
+
+// An abstracted GetVariable interface that enables configuration regardless of the environment.
+EFI_GET_VARIABLE mGetVariableHelper = NULL;
+
+// Master switch to lock this entire interface. Does not stop enforcement,
+// just prevents the configuration from being changed for the rest of the boot.
+STATIC BOOLEAN mInterfaceLocked = FALSE;
+
+// Master switch to disable the entire interface for a single boot.
+// This will disable all policy enforcement for the duration of the boot.
+STATIC BOOLEAN mProtectionDisabled = FALSE;
+
+// Table to hold all the current policies.
+UINT8 *mPolicyTable = NULL;
+STATIC UINT32 mCurrentTableSize = 0;
+STATIC UINT32 mCurrentTableUsage = 0;
+STATIC UINT32 mCurrentTableCount = 0;
+
+#define POLICY_TABLE_STEP_SIZE 0x1000
+
+// NOTE: DO NOT USE THESE MACROS on any structure that has not been validated.
+// Current table data has already been sanitized.
+#define GET_NEXT_POLICY(CurPolicy) (VARIABLE_POLICY_ENTRY*)((UINT8*)CurPolicy + CurPolicy->Size)
+#define GET_POLICY_NAME(CurPolicy) (CHAR16*)((UINTN)CurPolicy + CurPolicy->OffsetToName)
+
+#define MATCH_PRIORITY_EXACT 0
+#define MATCH_PRIORITY_MAX MATCH_PRIORITY_EXACT
+#define MATCH_PRIORITY_MIN MAX_UINT8
+
+/**
+ An extra init hook that enables the RuntimeDxe library instance to
+ register VirtualAddress change callbacks. Among other things.
+
+ @retval EFI_SUCCESS Everything is good. Continue with init.
+ @retval Others Uh... don't continue.
+
+**/
+EFI_STATUS
+VariablePolicyExtraInit (
+ VOID
+ );
+
+/**
+ An extra deinit hook that enables the RuntimeDxe library instance to
+ register VirtualAddress change callbacks. Among other things.
+
+ @retval EFI_SUCCESS Everything is good. Continue with deinit.
+ @retval Others Uh... don't continue.
+
+**/
+EFI_STATUS
+VariablePolicyExtraDeinit (
+ VOID
+ );
+
+/**
+ This helper function determines whether the structure of an incoming policy
+ is valid and internally consistent.
+
+ @param[in] NewPolicy Pointer to the incoming policy structure.
+
+ @retval TRUE
+ @retval FALSE Pointer is NULL, size is wrong, strings are empty, or
+ substructures overlap.
+
+**/
+STATIC
+BOOLEAN
+IsValidVariablePolicyStructure (
+ IN CONST VARIABLE_POLICY_ENTRY *NewPolicy
+ )
+{
+ EFI_STATUS Status;
+ UINTN EntryEnd;
+ CHAR16 *CheckChar;
+ UINTN WildcardCount;
+
+ // Sanitize some quick values.
+ if ((NewPolicy == NULL) || (NewPolicy->Size == 0) ||
+ // Structure size should be at least as long as the minumum structure and a NULL string.
+ (NewPolicy->Size < sizeof (VARIABLE_POLICY_ENTRY)) ||
+ // Check for the known revision.
+ (NewPolicy->Version != VARIABLE_POLICY_ENTRY_REVISION))
+ {
+ return FALSE;
+ }
+
+ // Calculate the theoretical end of the structure and make sure
+ // that the structure can fit in memory.
+ Status = SafeUintnAdd ((UINTN)NewPolicy, NewPolicy->Size, &EntryEnd);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ // Check for a valid Max Size.
+ if (NewPolicy->MaxSize == 0) {
+ return FALSE;
+ }
+
+ // Check for the valid list of lock policies.
+ if ((NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_NO_LOCK) &&
+ (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_NOW) &&
+ (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) &&
+ (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE))
+ {
+ return FALSE;
+ }
+
+ // If the policy type is VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE, make sure that the matching state variable Name
+ // terminates before the OffsetToName for the matching policy variable Name.
+ if (NewPolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {
+ // Adjust CheckChar to the offset of the LockPolicy->Name.
+ Status = SafeUintnAdd (
+ (UINTN)NewPolicy + sizeof (VARIABLE_POLICY_ENTRY),
+ sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY),
+ (UINTN *)&CheckChar
+ );
+ if (EFI_ERROR (Status) || (EntryEnd <= (UINTN)CheckChar)) {
+ return FALSE;
+ }
+
+ while (*CheckChar != CHAR_NULL) {
+ if (EntryEnd <= (UINTN)CheckChar) {
+ return FALSE;
+ }
+
+ CheckChar++;
+ }
+
+ // At this point we should have either exeeded the structure or be pointing at the last char in LockPolicy->Name.
+ // We should check to make sure that the policy Name comes immediately after this charcter.
+ if ((UINTN)++ CheckChar != (UINTN)NewPolicy + NewPolicy->OffsetToName) {
+ return FALSE;
+ }
+
+ // If the policy type is any other value, make sure that the LockPolicy structure has a zero length.
+ } else {
+ if (NewPolicy->OffsetToName != sizeof (VARIABLE_POLICY_ENTRY)) {
+ return FALSE;
+ }
+ }
+
+ // Check to make sure that the name has a terminating character
+ // before the end of the structure.
+ // We've already checked that the name is within the bounds of the structure.
+ if (NewPolicy->Size != NewPolicy->OffsetToName) {
+ CheckChar = (CHAR16 *)((UINTN)NewPolicy + NewPolicy->OffsetToName);
+ WildcardCount = 0;
+ while (*CheckChar != CHAR_NULL) {
+ // Make sure there aren't excessive wildcards.
+ if (*CheckChar == '#') {
+ WildcardCount++;
+ if (WildcardCount > MATCH_PRIORITY_MIN) {
+ return FALSE;
+ }
+ }
+
+ // Make sure you're still within the bounds of the policy structure.
+ if (EntryEnd <= (UINTN)CheckChar) {
+ return FALSE;
+ }
+
+ CheckChar++;
+ }
+
+ // Finally, we should be pointed at the very last character in Name, so we should be right
+ // up against the end of the structure.
+ if ((UINTN)++ CheckChar != EntryEnd) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ This helper function evaluates a policy and determines whether it matches the target
+ variable. If matched, will also return a value corresponding to the priority of the match.
+
+ The rules for "best match" are listed in the Variable Policy Spec.
+ Perfect name matches will return 0.
+ Single wildcard characters will return the number of wildcard characters.
+ Full namespaces will return MAX_UINT8.
+
+ @param[in] EvalEntry Pointer to the policy entry being evaluated.
+ @param[in] VariableName Same as EFI_SET_VARIABLE.
+ @param[in] VendorGuid Same as EFI_SET_VARIABLE.
+ @param[out] MatchPriority [Optional] On finding a match, this value contains the priority of the match.
+ Lower number == higher priority. Only valid if a match found.
+
+ @retval TRUE Current entry matches the target variable.
+ @retval FALSE Current entry does not match at all.
+
+**/
+STATIC
+BOOLEAN
+EvaluatePolicyMatch (
+ IN CONST VARIABLE_POLICY_ENTRY *EvalEntry,
+ IN CONST CHAR16 *VariableName,
+ IN CONST EFI_GUID *VendorGuid,
+ OUT UINT8 *MatchPriority OPTIONAL
+ )
+{
+ BOOLEAN Result;
+ CHAR16 *PolicyName;
+ UINT8 CalculatedPriority;
+ UINTN Index;
+
+ Result = FALSE;
+ CalculatedPriority = MATCH_PRIORITY_EXACT;
+
+ // Step 1: If the GUID doesn't match, we're done. No need to evaluate anything else.
+ if (!CompareGuid (&EvalEntry->Namespace, VendorGuid)) {
+ goto Exit;
+ }
+
+ // If the GUID matches, check to see whether there is a Name associated
+ // with the policy. If not, this policy matches the entire namespace.
+ // Missing Name is indicated by size being equal to name.
+ if (EvalEntry->Size == EvalEntry->OffsetToName) {
+ CalculatedPriority = MATCH_PRIORITY_MIN;
+ Result = TRUE;
+ goto Exit;
+ }
+
+ // Now that we know the name exists, get it.
+ PolicyName = GET_POLICY_NAME (EvalEntry);
+
+ // Evaluate the name against the policy name and check for a match.
+ // Account for any wildcards.
+ Index = 0;
+ Result = TRUE;
+ // Keep going until the end of both strings.
+ while (PolicyName[Index] != CHAR_NULL || VariableName[Index] != CHAR_NULL) {
+ // If we don't have a match...
+ if ((PolicyName[Index] != VariableName[Index]) || (PolicyName[Index] == '#')) {
+ // If this is a numerical wildcard, we can consider
+ // it a match if we alter the priority.
+ if ((PolicyName[Index] == L'#') &&
+ (((L'0' <= VariableName[Index]) && (VariableName[Index] <= L'9')) ||
+ ((L'A' <= VariableName[Index]) && (VariableName[Index] <= L'F')) ||
+ ((L'a' <= VariableName[Index]) && (VariableName[Index] <= L'f'))))
+ {
+ if (CalculatedPriority < MATCH_PRIORITY_MIN) {
+ CalculatedPriority++;
+ }
+
+ // Otherwise, not a match.
+ } else {
+ Result = FALSE;
+ goto Exit;
+ }
+ }
+
+ Index++;
+ }
+
+Exit:
+ if (Result && (MatchPriority != NULL)) {
+ *MatchPriority = CalculatedPriority;
+ }
+
+ return Result;
+}
+
+/**
+ This helper function walks the current policy table and returns a pointer
+ to the best match, if any are found. Leverages EvaluatePolicyMatch() to
+ determine "best".
+
+ @param[in] VariableName Same as EFI_SET_VARIABLE.
+ @param[in] VendorGuid Same as EFI_SET_VARIABLE.
+ @param[out] ReturnPriority [Optional] If pointer is provided, return the
+ priority of the match. Same as EvaluatePolicyMatch().
+ Only valid if a match is returned.
+
+ @retval VARIABLE_POLICY_ENTRY* Best match that was found.
+ @retval NULL No match was found.
+
+**/
+STATIC
+VARIABLE_POLICY_ENTRY *
+GetBestPolicyMatch (
+ IN CONST CHAR16 *VariableName,
+ IN CONST EFI_GUID *VendorGuid,
+ OUT UINT8 *ReturnPriority OPTIONAL
+ )
+{
+ VARIABLE_POLICY_ENTRY *BestResult;
+ VARIABLE_POLICY_ENTRY *CurrentEntry;
+ UINT8 MatchPriority;
+ UINT8 CurrentPriority;
+ UINTN Index;
+
+ BestResult = NULL;
+ MatchPriority = MATCH_PRIORITY_EXACT;
+
+ // Walk all entries in the table, looking for matches.
+ CurrentEntry = (VARIABLE_POLICY_ENTRY *)mPolicyTable;
+ for (Index = 0; Index < mCurrentTableCount; Index++) {
+ // Check for a match.
+ if (EvaluatePolicyMatch (CurrentEntry, VariableName, VendorGuid, &CurrentPriority)) {
+ // If match is better, take it.
+ if ((BestResult == NULL) || (CurrentPriority < MatchPriority)) {
+ BestResult = CurrentEntry;
+ MatchPriority = CurrentPriority;
+ }
+
+ // If you've hit the highest-priority match, can exit now.
+ if (MatchPriority == 0) {
+ break;
+ }
+ }
+
+ // If we're still in the loop, move to the next entry.
+ CurrentEntry = GET_NEXT_POLICY (CurrentEntry);
+ }
+
+ // If a return priority was requested, return it.
+ if (ReturnPriority != NULL) {
+ *ReturnPriority = MatchPriority;
+ }
+
+ return BestResult;
+}
+
+/**
+ This API function validates and registers a new policy with
+ the policy enforcement engine.
+
+ @param[in] NewPolicy Pointer to the incoming policy structure.
+
+ @retval EFI_SUCCESS
+ @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.
+ @retval EFI_ALREADY_STARTED An identical matching policy already exists.
+ @retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.
+ @retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies.
+ @retval EFI_ABORTED A calculation error has prevented this function from completing.
+ @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.
+ @retval EFI_NOT_READY Library has not yet been initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+RegisterVariablePolicy (
+ IN CONST VARIABLE_POLICY_ENTRY *NewPolicy
+ )
+{
+ EFI_STATUS Status;
+ VARIABLE_POLICY_ENTRY *MatchPolicy;
+ UINT8 MatchPriority;
+ UINT32 NewSize;
+ UINT8 *NewTable;
+
+ if (!IsVariablePolicyLibInitialized ()) {
+ return EFI_NOT_READY;
+ }
+
+ if (mInterfaceLocked) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ if (!IsValidVariablePolicyStructure (NewPolicy)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Check to see whether an exact matching policy already exists.
+ MatchPolicy = GetBestPolicyMatch (
+ GET_POLICY_NAME (NewPolicy),
+ &NewPolicy->Namespace,
+ &MatchPriority
+ );
+ if ((MatchPolicy != NULL) && (MatchPriority == MATCH_PRIORITY_EXACT)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ // If none exists, create it.
+ // If we need more space, allocate that now.
+ Status = SafeUint32Add (mCurrentTableUsage, NewPolicy->Size, &NewSize);
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+
+ if (NewSize > mCurrentTableSize) {
+ // Use NewSize to calculate the new table size in units of POLICY_TABLE_STEP_SIZE.
+ NewSize = (NewSize % POLICY_TABLE_STEP_SIZE) > 0 ?
+ (NewSize / POLICY_TABLE_STEP_SIZE) + 1 :
+ (NewSize / POLICY_TABLE_STEP_SIZE);
+ // Calculate the new table size in absolute bytes.
+ Status = SafeUint32Mult (NewSize, POLICY_TABLE_STEP_SIZE, &NewSize);
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+
+ // Reallocate and copy the table.
+ NewTable = AllocateRuntimePool (NewSize);
+ if (NewTable == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (NewTable, mPolicyTable, mCurrentTableUsage);
+ mCurrentTableSize = NewSize;
+ if (mPolicyTable != NULL) {
+ FreePool (mPolicyTable);
+ }
+
+ mPolicyTable = NewTable;
+ }
+
+ // Copy the policy into the table.
+ CopyMem (mPolicyTable + mCurrentTableUsage, NewPolicy, NewPolicy->Size);
+ mCurrentTableUsage += NewPolicy->Size;
+ mCurrentTableCount += 1;
+
+ // We're done here.
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This API function checks to see whether the parameters to SetVariable would
+ be allowed according to the current variable policies.
+
+ @param[in] VariableName Same as EFI_SET_VARIABLE.
+ @param[in] VendorGuid Same as EFI_SET_VARIABLE.
+ @param[in] Attributes Same as EFI_SET_VARIABLE.
+ @param[in] DataSize Same as EFI_SET_VARIABLE.
+ @param[in] Data Same as EFI_SET_VARIABLE.
+
+ @retval EFI_SUCCESS A matching policy allows this update.
+ @retval EFI_SUCCESS There are currently no policies that restrict this update.
+ @retval EFI_SUCCESS The protections have been disable until the next reboot.
+ @retval EFI_WRITE_PROTECTED Variable is currently locked.
+ @retval EFI_INVALID_PARAMETER Attributes or size are invalid.
+ @retval EFI_ABORTED A lock policy exists, but an error prevented evaluation.
+ @retval EFI_NOT_READY Library has not been initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+ValidateSetVariable (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ BOOLEAN IsDel;
+ VARIABLE_POLICY_ENTRY *ActivePolicy;
+ EFI_STATUS Status;
+ EFI_STATUS ReturnStatus;
+ VARIABLE_LOCK_ON_VAR_STATE_POLICY *StateVarPolicy;
+ CHAR16 *StateVarName;
+ UINTN StateVarSize;
+ UINT8 StateVar;
+
+ ReturnStatus = EFI_SUCCESS;
+
+ if (!IsVariablePolicyLibInitialized ()) {
+ ReturnStatus = EFI_NOT_READY;
+ goto Exit;
+ }
+
+ // Bail if the protections are currently disabled.
+ if (mProtectionDisabled) {
+ ReturnStatus = EFI_SUCCESS;
+ goto Exit;
+ }
+
+ // Determine whether this is a delete operation.
+ // If so, it will affect which tests are applied.
+ if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {
+ IsDel = TRUE;
+ } else {
+ IsDel = FALSE;
+ }
+
+ // Find an active policy if one exists.
+ ActivePolicy = GetBestPolicyMatch (VariableName, VendorGuid, NULL);
+
+ // If we have an active policy, check it against the incoming data.
+ if (ActivePolicy != NULL) {
+ //
+ // Only enforce size and attribute constraints when updating data, not deleting.
+ if (!IsDel) {
+ // Check for size constraints.
+ if (((ActivePolicy->MinSize > 0) && (DataSize < ActivePolicy->MinSize)) ||
+ ((ActivePolicy->MaxSize > 0) && (DataSize > ActivePolicy->MaxSize)))
+ {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a - Bad Size. 0x%X <> 0x%X-0x%X\n",
+ __func__,
+ DataSize,
+ ActivePolicy->MinSize,
+ ActivePolicy->MaxSize
+ ));
+ goto Exit;
+ }
+
+ // Check for attribute constraints.
+ if (((ActivePolicy->AttributesMustHave & Attributes) != ActivePolicy->AttributesMustHave) ||
+ ((ActivePolicy->AttributesCantHave & Attributes) != 0))
+ {
+ ReturnStatus = EFI_INVALID_PARAMETER;
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a - Bad Attributes. 0x%X <> 0x%X:0x%X\n",
+ __func__,
+ Attributes,
+ ActivePolicy->AttributesMustHave,
+ ActivePolicy->AttributesCantHave
+ ));
+ goto Exit;
+ }
+ }
+
+ //
+ // Lock policy check.
+ //
+ // Check for immediate lock.
+ if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_NOW) {
+ ReturnStatus = EFI_WRITE_PROTECTED;
+ goto Exit;
+ // Check for lock on create.
+ } else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) {
+ StateVarSize = 0;
+ Status = mGetVariableHelper (
+ VariableName,
+ VendorGuid,
+ NULL,
+ &StateVarSize,
+ NULL
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ ReturnStatus = EFI_WRITE_PROTECTED;
+ goto Exit;
+ }
+
+ // Check for lock on state variable.
+ } else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {
+ StateVarPolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY *)((UINT8 *)ActivePolicy + sizeof (VARIABLE_POLICY_ENTRY));
+ StateVarName = (CHAR16 *)((UINT8 *)StateVarPolicy + sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY));
+ StateVarSize = sizeof (StateVar);
+ Status = mGetVariableHelper (
+ StateVarName,
+ &StateVarPolicy->Namespace,
+ NULL,
+ &StateVarSize,
+ &StateVar
+ );
+
+ // If the variable was found, check the state. If matched, this variable is locked.
+ if (!EFI_ERROR (Status)) {
+ if (StateVar == StateVarPolicy->Value) {
+ ReturnStatus = EFI_WRITE_PROTECTED;
+ goto Exit;
+ }
+
+ // EFI_NOT_FOUND and EFI_BUFFER_TOO_SMALL indicate that the state doesn't match.
+ } else if ((Status != EFI_NOT_FOUND) && (Status != EFI_BUFFER_TOO_SMALL)) {
+ // We don't know what happened, but it isn't good.
+ ReturnStatus = EFI_ABORTED;
+ goto Exit;
+ }
+ }
+ }
+
+Exit:
+ DEBUG ((DEBUG_VERBOSE, "%a - Variable (%g:%s) returning %r.\n", __func__, VendorGuid, VariableName, ReturnStatus));
+ return ReturnStatus;
+}
+
+/**
+ This API function disables the variable policy enforcement. If it's
+ already been called once, will return EFI_ALREADY_STARTED.
+
+ @retval EFI_SUCCESS
+ @retval EFI_ALREADY_STARTED Has already been called once this boot.
+ @retval EFI_WRITE_PROTECTED Interface has been locked until reboot.
+ @retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.
+ @retval EFI_NOT_READY Library has not yet been initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+DisableVariablePolicy (
+ VOID
+ )
+{
+ if (!IsVariablePolicyLibInitialized ()) {
+ return EFI_NOT_READY;
+ }
+
+ if (mProtectionDisabled) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ if (mInterfaceLocked) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ if (!PcdGetBool (PcdAllowVariablePolicyEnforcementDisable)) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ mProtectionDisabled = TRUE;
+ return EFI_SUCCESS;
+}
+
+/**
+ This API function will dump the entire contents of the variable policy table.
+
+ Similar to GetVariable, the first call can be made with a 0 size and it will return
+ the size of the buffer required to hold the entire table.
+
+ @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.
+ @param[in,out] Size On input, the size of the output buffer. On output, the size
+ of the data returned.
+
+ @retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.
+ @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.
+ @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.
+ @retval EFI_NOT_READY Library has not yet been initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+DumpVariablePolicy (
+ OUT UINT8 *Policy,
+ IN OUT UINT32 *Size
+ )
+{
+ if (!IsVariablePolicyLibInitialized ()) {
+ return EFI_NOT_READY;
+ }
+
+ // Check the parameters.
+ if ((Size == NULL) || ((*Size > 0) && (Policy == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Make sure the size is sufficient to hold the policy table.
+ if (*Size < mCurrentTableUsage) {
+ *Size = mCurrentTableUsage;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ // If we're still here, copy the table and bounce.
+ CopyMem (Policy, mPolicyTable, mCurrentTableUsage);
+ *Size = mCurrentTableUsage;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function will return variable policy information for a UEFI variable with a
+ registered variable policy.
+
+ @param[in] VariableName The name of the variable to use for the policy search.
+ @param[in] VendorGuid The vendor GUID of the variable to use for the policy search.
+ @param[in,out] VariablePolicyVariableNameBufferSize On input, the size, in bytes, of the VariablePolicyVariableName
+ buffer.
+
+ On output, the size, in bytes, needed to store the variable
+ policy variable name.
+
+ If testing for the VariablePolicyVariableName buffer size
+ needed, set this value to zero so EFI_BUFFER_TOO_SMALL is
+ guaranteed to be returned if the variable policy variable name
+ is found.
+ @param[out] VariablePolicy Pointer to a buffer where the policy entry will be written
+ if found.
+ @param[out] VariablePolicyVariableName Pointer to a buffer where the variable name used for the
+ variable policy will be written if a variable name is
+ registered.
+
+ If the variable policy is not associated with a variable name
+ (e.g. applied to variable vendor namespace) and this parameter
+ is given, this parameter will not be modified and
+ VariablePolicyVariableNameBufferSize will be set to zero to
+ indicate a name was not present.
+
+ If the pointer given is not NULL,
+ VariablePolicyVariableNameBufferSize must be non-NULL.
+
+ @retval EFI_SUCCESS A variable policy entry was found and returned successfully.
+ @retval EFI_BAD_BUFFER_SIZE An internal buffer size caused a calculation error.
+ @retval EFI_BUFFER_TOO_SMALL The VariablePolicyVariableName buffer value is too small for the size needed.
+ The buffer should now point to the size needed.
+ @retval EFI_NOT_READY Variable policy has not yet been initialized.
+ @retval EFI_INVALID_PARAMETER A required pointer argument passed is NULL. This will be returned if
+ VariablePolicyVariableName is non-NULL and VariablePolicyVariableNameBufferSize
+ is NULL.
+ @retval EFI_NOT_FOUND A variable policy was not found for the given UEFI variable name and vendor GUID.
+
+**/
+EFI_STATUS
+EFIAPI
+GetVariablePolicyInfo (
+ IN CONST CHAR16 *VariableName,
+ IN CONST EFI_GUID *VendorGuid,
+ IN OUT UINTN *VariablePolicyVariableNameBufferSize OPTIONAL,
+ OUT VARIABLE_POLICY_ENTRY *VariablePolicy,
+ OUT CHAR16 *VariablePolicyVariableName OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UINT8 MatchPriority;
+ UINTN LocalVariablePolicyVariableNameBufferSize;
+ UINTN RequiredVariablePolicyVariableNameBufferSize;
+ VARIABLE_POLICY_ENTRY *MatchPolicy;
+
+ Status = EFI_SUCCESS;
+
+ if (!IsVariablePolicyLibInitialized ()) {
+ return EFI_NOT_READY;
+ }
+
+ if ((VariableName == NULL) || (VendorGuid == NULL) || (VariablePolicy == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MatchPolicy = GetBestPolicyMatch (
+ VariableName,
+ VendorGuid,
+ &MatchPriority
+ );
+ if (MatchPolicy != NULL) {
+ CopyMem (VariablePolicy, MatchPolicy, sizeof (*VariablePolicy));
+
+ if (VariablePolicyVariableNameBufferSize == NULL) {
+ if (VariablePolicyVariableName != NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return Status;
+ }
+
+ if (MatchPolicy->Size != MatchPolicy->OffsetToName) {
+ if (MatchPolicy->Size < MatchPolicy->OffsetToName) {
+ ASSERT (MatchPolicy->Size > MatchPolicy->OffsetToName);
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ RequiredVariablePolicyVariableNameBufferSize = (UINTN)(MatchPolicy->Size - MatchPolicy->OffsetToName);
+ ASSERT (RequiredVariablePolicyVariableNameBufferSize > 0);
+
+ if (*VariablePolicyVariableNameBufferSize < RequiredVariablePolicyVariableNameBufferSize) {
+ // Let the caller get the size needed to hold the policy variable name
+ *VariablePolicyVariableNameBufferSize = RequiredVariablePolicyVariableNameBufferSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ if (VariablePolicyVariableName == NULL) {
+ // If the policy variable name size given is valid, then a valid policy variable name buffer should be provided
+ *VariablePolicyVariableNameBufferSize = RequiredVariablePolicyVariableNameBufferSize;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ LocalVariablePolicyVariableNameBufferSize = *VariablePolicyVariableNameBufferSize;
+
+ // Actual string size should match expected string size
+ if (
+ ((StrnLenS (GET_POLICY_NAME (MatchPolicy), RequiredVariablePolicyVariableNameBufferSize) + 1) * sizeof (CHAR16))
+ != RequiredVariablePolicyVariableNameBufferSize)
+ {
+ ASSERT_EFI_ERROR (EFI_BAD_BUFFER_SIZE);
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ *VariablePolicyVariableNameBufferSize = RequiredVariablePolicyVariableNameBufferSize;
+
+ Status = StrnCpyS (
+ VariablePolicyVariableName,
+ LocalVariablePolicyVariableNameBufferSize / sizeof (CHAR16),
+ GET_POLICY_NAME (MatchPolicy),
+ RequiredVariablePolicyVariableNameBufferSize / sizeof (CHAR16)
+ );
+ ASSERT_EFI_ERROR (Status);
+ } else {
+ // A variable policy variable name is not present. Return values according to interface.
+ *VariablePolicyVariableNameBufferSize = 0;
+ }
+
+ return Status;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ This function will return the Lock on Variable State policy information for the policy
+ associated with the given UEFI variable.
+
+ @param[in] VariableName The name of the variable to use for the policy search.
+ @param[in] VendorGuid The vendor GUID of the variable to use for the policy
+ search.
+ @param[in,out] VariableLockPolicyVariableNameBufferSize On input, the size, in bytes, of the
+ VariableLockPolicyVariableName buffer.
+
+ On output, the size, in bytes, needed to store the variable
+ policy variable name.
+
+ If testing for the VariableLockPolicyVariableName buffer
+ size needed, set this value to zero so EFI_BUFFER_TOO_SMALL
+ is guaranteed to be returned if the variable policy variable
+ name is found.
+ @param[out] VariablePolicy Pointer to a buffer where the policy entry will be written
+ if found.
+ @param[out] VariableLockPolicyVariableName Pointer to a buffer where the variable name used for the
+ variable lock on variable state policy will be written if
+ a variable name is registered.
+
+ If the lock on variable policy is not associated with a
+ variable name (e.g. applied to variable vendor namespace)
+ and this parameter is given, this parameter will not be
+ modified and VariableLockPolicyVariableNameBufferSize will
+ be set to zero to indicate a name was not present.
+
+ If the pointer given is not NULL,
+ VariableLockPolicyVariableNameBufferSize must be non-NULL.
+
+ @retval EFI_SUCCESS A Lock on Variable State variable policy entry was found and returned
+ successfully.
+ @retval EFI_BAD_BUFFER_SIZE An internal buffer size caused a calculation error.
+ @retval EFI_BUFFER_TOO_SMALL The VariableLockPolicyVariableName buffer is too small for the size needed.
+ The buffer should now point to the size needed.
+ @retval EFI_NOT_READY Variable policy has not yet been initialized.
+ @retval EFI_INVALID_PARAMETER A required pointer argument passed is NULL. This will be returned if
+ VariableLockPolicyVariableName is non-NULL and
+ VariableLockPolicyVariableNameBufferSize is NULL.
+ @retval EFI_NOT_FOUND A Lock on Variable State variable policy was not found for the given UEFI
+ variable name and vendor GUID.
+
+**/
+EFI_STATUS
+EFIAPI
+GetLockOnVariableStateVariablePolicyInfo (
+ IN CONST CHAR16 *VariableName,
+ IN CONST EFI_GUID *VendorGuid,
+ IN OUT UINTN *VariableLockPolicyVariableNameBufferSize OPTIONAL,
+ OUT VARIABLE_LOCK_ON_VAR_STATE_POLICY *VariablePolicy,
+ OUT CHAR16 *VariableLockPolicyVariableName OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UINT8 MatchPriority;
+ UINTN RequiredVariablePolicyVariableNameBufferSize;
+ UINTN RequiredVariableLockPolicyVariableNameBufferSize;
+ UINTN LocalVariablePolicyLockVariableNameBufferSize;
+ UINTN LockOnVarStatePolicyEndOffset;
+ CHAR16 *LocalVariableLockPolicyVariableName;
+ VARIABLE_LOCK_ON_VAR_STATE_POLICY *LocalLockOnVarStatePolicy;
+ VARIABLE_POLICY_ENTRY *MatchPolicy;
+
+ Status = EFI_SUCCESS;
+
+ if (!IsVariablePolicyLibInitialized ()) {
+ return EFI_NOT_READY;
+ }
+
+ if ((VariableName == NULL) || (VendorGuid == NULL) || (VariablePolicy == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MatchPolicy = GetBestPolicyMatch (
+ VariableName,
+ VendorGuid,
+ &MatchPriority
+ );
+ if (MatchPolicy != NULL) {
+ if (MatchPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {
+ return EFI_NOT_FOUND;
+ }
+
+ Status = SafeUintnAdd (
+ sizeof (VARIABLE_POLICY_ENTRY),
+ sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY),
+ &LockOnVarStatePolicyEndOffset
+ );
+ if (EFI_ERROR (Status) || (LockOnVarStatePolicyEndOffset > (UINTN)MatchPolicy->Size)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ LocalLockOnVarStatePolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY *)(MatchPolicy + 1);
+ CopyMem (VariablePolicy, LocalLockOnVarStatePolicy, sizeof (*LocalLockOnVarStatePolicy));
+
+ if ((VariableLockPolicyVariableNameBufferSize == NULL)) {
+ if (VariableLockPolicyVariableName != NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return Status;
+ }
+
+ // The name offset should be less than or equal to the total policy size.
+ if (MatchPolicy->Size < MatchPolicy->OffsetToName) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ RequiredVariablePolicyVariableNameBufferSize = (UINTN)(MatchPolicy->Size - MatchPolicy->OffsetToName);
+ RequiredVariableLockPolicyVariableNameBufferSize = MatchPolicy->Size -
+ (LockOnVarStatePolicyEndOffset + RequiredVariablePolicyVariableNameBufferSize);
+
+ LocalVariablePolicyLockVariableNameBufferSize = *VariableLockPolicyVariableNameBufferSize;
+ *VariableLockPolicyVariableNameBufferSize = RequiredVariableLockPolicyVariableNameBufferSize;
+
+ if (LocalVariablePolicyLockVariableNameBufferSize < RequiredVariableLockPolicyVariableNameBufferSize) {
+ // Let the caller get the size needed to hold the policy variable name
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ if (VariableLockPolicyVariableName == NULL) {
+ // If the policy variable name size given is valid, then a valid policy variable name buffer should be provided
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (RequiredVariableLockPolicyVariableNameBufferSize == 0) {
+ return Status;
+ }
+
+ LocalVariableLockPolicyVariableName = (CHAR16 *)((UINT8 *)LocalLockOnVarStatePolicy + sizeof (*LocalLockOnVarStatePolicy));
+ *VariableLockPolicyVariableNameBufferSize = RequiredVariableLockPolicyVariableNameBufferSize;
+
+ // Actual string size should match expected string size (if a variable name is present)
+ if (
+ (RequiredVariablePolicyVariableNameBufferSize > 0) &&
+ (((StrnLenS (GET_POLICY_NAME (MatchPolicy), RequiredVariablePolicyVariableNameBufferSize) + 1) * sizeof (CHAR16)) !=
+ RequiredVariablePolicyVariableNameBufferSize))
+ {
+ ASSERT_EFI_ERROR (EFI_BAD_BUFFER_SIZE);
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ // Actual string size should match expected string size (if here, variable lock variable name is present)
+ if (
+ ((StrnLenS (LocalVariableLockPolicyVariableName, RequiredVariableLockPolicyVariableNameBufferSize) + 1) * sizeof (CHAR16)) !=
+ RequiredVariableLockPolicyVariableNameBufferSize)
+ {
+ ASSERT_EFI_ERROR (EFI_BAD_BUFFER_SIZE);
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ Status = StrnCpyS (
+ VariableLockPolicyVariableName,
+ LocalVariablePolicyLockVariableNameBufferSize / sizeof (CHAR16),
+ LocalVariableLockPolicyVariableName,
+ RequiredVariableLockPolicyVariableNameBufferSize / sizeof (CHAR16)
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ This API function returns whether or not the policy engine is
+ currently being enforced.
+
+ @retval TRUE
+ @retval FALSE
+ @retval FALSE Library has not yet been initialized.
+
+**/
+BOOLEAN
+EFIAPI
+IsVariablePolicyEnabled (
+ VOID
+ )
+{
+ if (!IsVariablePolicyLibInitialized ()) {
+ return FALSE;
+ }
+
+ return !mProtectionDisabled;
+}
+
+/**
+ This API function locks the interface so that no more policy updates
+ can be performed or changes made to the enforcement until the next boot.
+
+ @retval EFI_SUCCESS
+ @retval EFI_NOT_READY Library has not yet been initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+LockVariablePolicy (
+ VOID
+ )
+{
+ if (!IsVariablePolicyLibInitialized ()) {
+ return EFI_NOT_READY;
+ }
+
+ if (mInterfaceLocked) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ mInterfaceLocked = TRUE;
+ return EFI_SUCCESS;
+}
+
+/**
+ This API function returns whether or not the policy interface is locked
+ for the remainder of the boot.
+
+ @retval TRUE
+ @retval FALSE
+ @retval FALSE Library has not yet been initialized.
+
+**/
+BOOLEAN
+EFIAPI
+IsVariablePolicyInterfaceLocked (
+ VOID
+ )
+{
+ if (!IsVariablePolicyLibInitialized ()) {
+ return FALSE;
+ }
+
+ return mInterfaceLocked;
+}
+
+/**
+ This helper function initializes the library and sets
+ up any required internal structures or handlers.
+
+ Also registers the internal pointer for the GetVariable helper.
+
+ @param[in] GetVariableHelper A function pointer matching the EFI_GET_VARIABLE prototype that will be used to
+ check policy criteria that involve the existence of other variables.
+
+ @retval EFI_SUCCESS
+ @retval EFI_ALREADY_STARTED The initialize function has been called more than once without a call to
+ deinitialize.
+
+**/
+EFI_STATUS
+EFIAPI
+InitVariablePolicyLib (
+ IN EFI_GET_VARIABLE GetVariableHelper
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ if (mGetVariableHelper != NULL) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ Status = VariablePolicyExtraInit ();
+ }
+
+ if (!EFI_ERROR (Status)) {
+ // Save an internal pointer to the GetVariableHelper.
+ mGetVariableHelper = GetVariableHelper;
+
+ // Initialize the global state.
+ mInterfaceLocked = FALSE;
+ mProtectionDisabled = FALSE;
+ mPolicyTable = NULL;
+ mCurrentTableSize = 0;
+ mCurrentTableUsage = 0;
+ mCurrentTableCount = 0;
+ }
+
+ return Status;
+}
+
+/**
+ This helper function returns whether or not the library is currently initialized.
+
+ @retval TRUE
+ @retval FALSE
+
+**/
+BOOLEAN
+EFIAPI
+IsVariablePolicyLibInitialized (
+ VOID
+ )
+{
+ return (mGetVariableHelper != NULL);
+}
+
+/**
+ This helper function tears down the library.
+
+ Should generally only be used for test harnesses.
+
+ @retval EFI_SUCCESS
+ @retval EFI_NOT_READY Deinitialize was called without first calling initialize.
+
+**/
+EFI_STATUS
+EFIAPI
+DeinitVariablePolicyLib (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ if (mGetVariableHelper == NULL) {
+ return EFI_NOT_READY;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ Status = VariablePolicyExtraDeinit ();
+ }
+
+ if (!EFI_ERROR (Status)) {
+ mGetVariableHelper = NULL;
+ mInterfaceLocked = FALSE;
+ mProtectionDisabled = FALSE;
+ mCurrentTableSize = 0;
+ mCurrentTableUsage = 0;
+ mCurrentTableCount = 0;
+
+ if (mPolicyTable != NULL) {
+ FreePool (mPolicyTable);
+ mPolicyTable = NULL;
+ }
+ }
+
+ return Status;
+}
diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
index 7048d555f0..3ca66fc1f5 100644
--- a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
@@ -1,49 +1,49 @@
-## @file VariablePolicyLib.inf
-# Business logic for Variable Policy enforcement.
-#
-# Copyright (c) Microsoft Corporation.
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-##
-
-
-[Defines]
- INF_VERSION = 0x00010017
- BASE_NAME = VariablePolicyLib
- FILE_GUID = E9ECD342-159A-4F24-9FDF-65724027C594
- VERSION_STRING = 1.0
- MODULE_TYPE = DXE_DRIVER
- LIBRARY_CLASS = VariablePolicyLib|DXE_DRIVER DXE_SMM_DRIVER MM_STANDALONE
-
-#
-# The following information is for reference only and not required by the build tools.
-#
-# VALID_ARCHITECTURES = ANY
-#
-
-
-[Sources]
- VariablePolicyLib.c
- VariablePolicyExtraInitNull.c
-
-
-[Packages]
- MdePkg/MdePkg.dec
- MdeModulePkg/MdeModulePkg.dec
-
-
-[LibraryClasses]
- BaseLib
- DebugLib
- BaseMemoryLib
- MemoryAllocationLib
- SafeIntLib
- PcdLib
-
-
-[Pcd]
- gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable ## CONSUMES
-
-
-[BuildOptions]
- MSFT:NOOPT_*_*_CC_FLAGS = -DINTERNAL_UNIT_TEST
- GCC:NOOPT_*_*_CC_FLAGS = -DINTERNAL_UNIT_TEST
+## @file VariablePolicyLib.inf
+# Business logic for Variable Policy enforcement.
+#
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+
+[Defines]
+ INF_VERSION = 0x00010017
+ BASE_NAME = VariablePolicyLib
+ FILE_GUID = E9ECD342-159A-4F24-9FDF-65724027C594
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_DRIVER
+ LIBRARY_CLASS = VariablePolicyLib|DXE_DRIVER DXE_SMM_DRIVER MM_STANDALONE
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = ANY
+#
+
+
+[Sources]
+ VariablePolicyLib.c
+ VariablePolicyExtraInitNull.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ SafeIntLib
+ PcdLib
+
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable ## CONSUMES
+
+
+[BuildOptions]
+ MSFT:NOOPT_*_*_CC_FLAGS = -DINTERNAL_UNIT_TEST
+ GCC:NOOPT_*_*_CC_FLAGS = -DINTERNAL_UNIT_TEST
diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni
index 2227ec4278..f94b5b66b6 100644
--- a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni
@@ -1,12 +1,12 @@
-// /** @file
-// VariablePolicyLib.uni
-//
-// Copyright (c) Microsoft Corporation.
-// SPDX-License-Identifier: BSD-2-Clause-Patent
-//
-// **/
-
-
-#string STR_MODULE_ABSTRACT #language en-US "Library containing the business logic for the VariablePolicy engine"
-
-#string STR_MODULE_DESCRIPTION #language en-US "Library containing the business logic for the VariablePolicy engine"
+// /** @file
+// VariablePolicyLib.uni
+//
+// Copyright (c) Microsoft Corporation.
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Library containing the business logic for the VariablePolicy engine"
+
+#string STR_MODULE_DESCRIPTION #language en-US "Library containing the business logic for the VariablePolicy engine"
diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf
index 8b83657418..b1b2d40f78 100644
--- a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf
@@ -1,51 +1,51 @@
-## @file VariablePolicyLibRuntimeDxe.inf
-# Business logic for Variable Policy enforcement.
-# This instance is specifically for RuntimeDxe and contains
-# extra routines to register for VirtualAddressChangeEvents.
-#
-# Copyright (c) Microsoft Corporation.
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-##
-
-
-[Defines]
- INF_VERSION = 0x00010017
- BASE_NAME = VariablePolicyLibRuntimeDxe
- FILE_GUID = 205F7F0E-8EAC-4914-8390-1B90DD7E2A27
- VERSION_STRING = 1.0
- MODULE_TYPE = DXE_RUNTIME_DRIVER
- LIBRARY_CLASS = VariablePolicyLib|DXE_RUNTIME_DRIVER
-
-#
-# The following information is for reference only and not required by the build tools.
-#
-# VALID_ARCHITECTURES = ANY
-#
-
-
-[Sources]
- VariablePolicyLib.c
- VariablePolicyExtraInitRuntimeDxe.c
-
-
-[Packages]
- MdePkg/MdePkg.dec
- MdeModulePkg/MdeModulePkg.dec
-
-
-[LibraryClasses]
- DebugLib
- BaseMemoryLib
- MemoryAllocationLib
- SafeIntLib
- UefiBootServicesTableLib
- UefiRuntimeServicesTableLib
- PcdLib
-
-
-[Pcd]
- gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable ## CONSUMES
-
-
-[Guids]
- gEfiEventVirtualAddressChangeGuid
+## @file VariablePolicyLibRuntimeDxe.inf
+# Business logic for Variable Policy enforcement.
+# This instance is specifically for RuntimeDxe and contains
+# extra routines to register for VirtualAddressChangeEvents.
+#
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+
+[Defines]
+ INF_VERSION = 0x00010017
+ BASE_NAME = VariablePolicyLibRuntimeDxe
+ FILE_GUID = 205F7F0E-8EAC-4914-8390-1B90DD7E2A27
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_RUNTIME_DRIVER
+ LIBRARY_CLASS = VariablePolicyLib|DXE_RUNTIME_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = ANY
+#
+
+
+[Sources]
+ VariablePolicyLib.c
+ VariablePolicyExtraInitRuntimeDxe.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ DebugLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ SafeIntLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ PcdLib
+
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable ## CONSUMES
+
+
+[Guids]
+ gEfiEventVirtualAddressChangeGuid